python中的函数

函数是组织好的,是一个固定的可重复使用的程序段,或称其为一个子程序,在接收了定义的参数后,实现单一或相关联功能,最后还可以返回定义的内容。

函数的主要作用:1,避免代码重复;2,方便修改;3,能保持代码的一致性。

函数主要为两类:内置函数和自定义函数,内置函数可以直接引用,下面重点说一下自定义函数。

 函数的定义函数的命名规则函数定义的基本语法格式函数的调用参数函数的作用域

递归函数嵌套函数闭包高阶函数匿名函数自执行函数回调函数常用内置函数

函数的定义:

  •  def 关键字开头,后面加函数名和括号以及冒号,括号中根据需要定义需要传入的参数
  • 函数体严格遵守缩进原则
  • 根据需要,return所需信息
  • 本质上,可以将函数名理解成是数据类型是“function”的特殊变量,函数名存储的是储存函数体的 “ 数据类型,函数名,函数的内存地址 ” ,函数体是对象。所以函数名作为一种变量可以 赋值 给其他变量,并且函数名也可以当做参数传递给特定的函数,另外,函数名也可以作为函数调用的结果,返回值是函数名。只有当函数名后面加上包含指定类型参数的括号,才能调用执行函数体。

函数的命名规则:

  • 字母数字下划线的组合,数字不能开头,不能用特殊字符
  • 不能使用保留字符
  • 字母一律小写
  • 私有函数在函数前加一个下划线_

函数定义的基本语法格式:

函数的调用:

  • 调用自定义函数,必须先对函数定义,再调用。顺序错,程序报错
  • 函数名加括号,进行函数的调用
  • 传入参数,一定根据定义函数时对参数的规定传入
  • 如果调用的函数有返回值,可通过定义变量进行接收(方便重复使用)或者直接在语句中使用

参数:

函数定义过程中的是形式参数(形参),起到占位符的作用,函数在调用过程中传入的参数是实际参数(实参)。python中的基本数据类型(字符串、数字、列表、元组、字典、集合)都可以作为实参直接传递给调用的函数,传递参数时可根据实际需要,以键值对形式传递。

在调用函数使用的参数类型又可以分为四类:必备参数、关键字参数、默认参数和不定长参数。

  • 必备参数必须传入的参数,以正确的顺序传入函数。调用时的数量必须和声明时的一样。
  • 默认参数(和必备参数相对应的),调用函数时,默认参数的值如果没有传入,则被认为是默认值。
  • 位置参数,按参数的指定顺序进行传递的参数。
  • 关键字参数,使用关键字参数允许函数调用时参数的顺序与声明时不一致,因为 Python 解释器能够用参数名匹配参数值。
  • 不定长参数,需要一个函数能处理比当初声明时更多的参数。这些参数叫做不定长参数,和上述2种参数不同,声明时不会命名。
  • 在函数定义、函数调用过程中,函数的排放顺序,要严格按照(位置参数,关键字参数 / 默认参数,不定长参数)。先传value参数,然后传key-value参数
  • 在函数调用时,括号中实际参数顺序必须是(基本数据类型形式的参数,键值对形式的参数),当默认参数前出现了关键字参数(键值对形式),则默认参数或者不传递进而使用默认值,如果传递的话,则同样要以键值对形式传递。当默认参数之前都是位置参数时(基本数据类型形式的参数),则需要传递默认参数的时候,可以用键值对,也可以用基本数据类型。另外,不定长参数前如果出现了键值对类型的实参,则只能再传递键值对类型的参数
  • 不定长参数类型可分为 *args,**kwargs两部分, *args 用于接收多余的基本数据类型的参数,以元组形式存储到变量args中;**kwargs用于接收多余的键值对实参,以字典形式存储到变量kwargs中。

上一个简单的例子:

 1 a = 8
 2 b =10
 3 def func (i, j,name1, age1,  n = 100, m = 50,*args,**kwargs):
 4 
 5     print("""
 6         必备参数---->%s  %s
 7         关键字参数---->%s  %s
 8         默认参数----->%s  %s
 9     """%(i,j,name1,age1,n,m))
10     print("*args------>",args)
11     print("**kwargs--->",kwargs)
12 func(a,[1,"来个列表"],age1 = 12,name1 = "sen",m =20,n = 5,c='你好',d=200)
View Code

函数的作用域:

命名空间,是存储变量的绑定关系的空间,不同变量的作用域不同,最主要的就是源于这个变量所在的命名空间决定的。查看变量作用域的方法,globals()  locals()

在python中,能独自开辟和定义作用域的只有函数、类、模块。

函数作用域的寻址顺序(LEGB):

  1. L(local),局部,即当前代码块中
  2. E(Enclosing function locals),嵌套的父级函数作用域
  3. G(global),全局,此是模块级别的
  4. B(build-in),系统固定模块中

       在内层函数局部拿到全局变量时,如果局部需要重新定义(初始化,此时会创建一个作用域在局部内的新变量,变量名和全局变量相同,但不是同一个),必须在其他的引用语句之前完成定义,即先赋值再引用,否则会出现局部变量的引用报错。如果要对全局变量进行改动,则要通过(global 变量名)提前对变量进行声明。类似的操作,在有多层嵌套函数时,要拿到本层函数的上一层函数的变量并修改,则用(nonlocal 变量)提前声明。在使用nonlocal 必须保证外部函数的变量在内部函数 执行 之前完成了初始化(内层函数的执行之前,而非定义之前完成外部变量的初始化)。使用nonlocal 绑定外层函数的变量时,必须保证所绑定的目标变量是外层函数的局部变量,而非经过global处理的全局变量,否则会出现找不到需要绑定的目标变量的错误。

  注意的是,如果引用的上一级参数,或者是全局参数时,如果被引用的参数是列表、字典、集合等可变数据类型,当改变引用参数的元素时,参数本身的内存地址即参数本身不会发生变化,但其内部元素会根据操作变化。

      来一段练习用的代码:

 1 a_global = 0
 2 def func_1():
 3     lst1 = ["第一层",1,2,3]
 4     #global a_global  #如果有对全局a_global进行修改的操作,提前声明
 5     # a_global = 1  # 定义或初始化,需要在所有的引用之前,否则会报错
 6     # print(a_global,id(a_global))
 7     print(lst1, id(lst1))
 8     def func_2():
 9         nonlocal a_global
10         print("第二层修改前------->",a_global,id(a_global))
11         a_global = 2
12         nonlocal lst1
13         lst1 = ["第二层","a",1,2,3]
14         print("第二层修改后------->",a_global,id(a_global))
15         print(lst1,id(lst1))
16     a_global = 11
17     print(a_global, id(a_global))
18     func_2()
19 
20     print(lst1,id(lst1))
21     print("第一层--------->",a_global,id(a_global))
22 func_1()
23 print(a_global,id(a_global))
View Code

 

递归函数:

  在函数内部可以调用函数,包括调用自身和调用其他函数。在函数内部调用当前函数本身,此类函数即递归函数。

      递归函数的基本特点:

  • 设置有明确的结束条件
  • 每进行一次运算,问题的规模都会有所减小
  • 递归的次数过多会造成栈溢出。(计算机中的函数调用时通过栈——stack又称堆栈,它是一种运算受限的线性表,这种数据结构实现的,调用一次加一层,返回退出一层减一层,调用过多会栈溢出。)

      形象点一个比喻,将两块镜子相对而放,(放好的一瞬间)各种重叠各种包含的现象出来了,当然,如果镜子完全是平行相对放着的,则两面镜子之间相互嵌套的层次是无限的,防止这种现象出现,就需要在放置镜子的时候不能是平行的。另外一个例子,“从前有座山,山上有座庙,庙里有个老和尚,老和尚再给小和尚讲故事:从前有座山……”,当故事中最后一个老和尚把他的故事讲完,外层讲故事的的老和尚也就把故事讲完了。

      通过上面的例子可以直观理解下递归函数,明显的优点是结构简单、逻辑上清晰,可读性好。但是,短板是,计算机的内存空间和运行空间是有限的,不能对无限次的嵌套进行存储和运算,否则会出现栈溢出的现象。这样就需要我们在定义递归函数的时候,设定好结束递归的最内层的限制条件。一般情况下,递归函数都是可以通过循环来实现的。此外,由于物理限定,当递归的次数上升后会严重影响计算机的运行效率,所以递归函数相对于循环实现,在大部分情况下效率是比较低的,除非在极个别的允许的递归次数内,单层执行耗时在不大不小的范围内,递归效率稍微好于循环。

      为了提升代码的执行效率,在一般情况下,尽量用循环来替代递归。

简单做个试验:

 1 import time
 2 def sum_cycle(n):
 3     '''
 4     实现 从1加到n,并返回结果
 5     '''
 6     sum = 0
 7     for i in range(1,n + 1):
 8         sum += i
 9         time.sleep(0.001) #代指功能代码块的运行过程
10     return sum
11 
12 def sum_recu(n):
13     '''
14     实现 从1加到n,并返回结果
15     '''
16     time.sleep(0.001) #代指功能代码块的运行过程
17     if n > 0:
18         return n + sum_recu(n - 1) #调用函数自身
19     else:
20         return 0
21 t_start = time.time()
22 print("循环求和:",sum_cycle(900))
23 t_stop = time.time()
24 print("循环用时----->",t_stop-t_start)
25 
26 t_start = time.time()
27 print("递归求和:",sum_recu(900))
28 t_stop = time.time()
29 print("递归用时----->",t_stop-t_start)
View Code

 

嵌套函数 :

在函数内部又定义了一个全新的函数,这种函数对象称为嵌套函数,此操作过程即函数的嵌套。一般情况下,如果外层函数没有将内部函数作为结果返回出函数体,那么内部函数的作用区域仅为当前函数体内。

 

闭包:

      闭包就是能够读取其他函数内部变量的函数。在本质上,闭包是一个嵌套函数,是将函数内部和函数外部连接起来的桥梁。闭包包含函数快(内部函数)及内部函数定义时的环境。

      简单理解为,如果在一个嵌套函数的内部函数里,对外部函数的局部变量(注意不是全局作用域的变量)进行引用,并且外部函数以内部函数名作为返回值,那么内部函数就被认为是闭包(closure)。此时,运行嵌套函数得到的结果是,以外部函数执行时形成的定义环境为背景定义的内部函数名,通过传入响应的参数进行调用可得到内部函数的运行结果。

      重要意义:可以通过外部函数,改变内部函数的定义环境,也就是改变了内部函数的某些定义参数。

上个简单小栗子:

 View Code

 

高阶函数

      高阶函数的基本定义包括两点(1接受一个或多个函数作为参数输入,2输出一个函数,注意不是函数执行结果而是函数体,简单点就是返回函数名),满足上述两点中  至少  一个条件的即称之为高阶函数。简单理解下,接收函数名作为参数的函数就是高阶函数,另外在函数执行返回的参数是函数名的也属于高阶函数。在函数式编程中,返回另一个函数的高阶函数被称为柯里化的函数。

      高阶函数的基本语法结构及其调用如下:

      高阶函数的主要作用:根据高阶函数的定义,满足接受一个或多个函数作为输入,这样就能在不改变原函数代码基础上,为原函数添加功能,如果能满足第二点,当返回的函数名和传入的函数名一致的时候,就实现了不改变元函数的调用方式的条件下实现新的功能。

      比较有代表性的内置高阶函数有:

  • map(),接收两个参数,一个是函数,一个是序列,map将传入的函数依次作用到序列的每个元素,返回结果,在python2.x中是list,而在python3.x中结果是以迭代器形式返回的是一个map对象,使用时可用list()强转。   基本格式:  map(函数名,序列)
  • reduce(),不同于python2.x,在python3.x中,reduce被移到了functools 模块里,可以通过调用模块来使用,作用是对序列中的元素反复调用函数 f ,并返回最终结果值。基本格式 :functools.reduce(函数名, 序列,“初始值”),其中第三个参数“初始值”是可选的
  • filter():用于过滤序列,过滤掉不符合条件的元素,在python2.x中返回由符合条件元素组成的新列表,python3.x中返回的是filter对象。基本格式:filter(过滤操作的执行函数名,序列)

匿名函数/lambda:

匿名函数,字面上理解,就是没有命名的函数,返回的是一个lambda函数对象。

  • 基本格式:lambda 参数1,参数2,参数3…… :函数体
  • 匿名函数的函数体只是一个表达式,不是代码块。
  • 那怎么去找到这个函数并执行呢,要么,把匿名函数赋值给一个变量,通过变量实现匿名函数的调用,要么将lambda函数直接作为变量传递(高阶函数中的函数名参数)
  • 匿名函数有自己的命名空间
  • 匿名函数的优点:1,可以精简代码  2,防止不重复使用的函数和其他函数出现命名重复,进而覆盖  3,在一定程度上增强了代码的可读性

来一个利用lambda函数构建高阶函数的简单栗子:

def func_1(i):
    return lambda x:x*i

 

自执行函数(立即执行函数):

一般地命名函数都是命名过程和调用过程分开的,但自执行函数或(立即执行函数)是函数构建完成后直接调用,一气呵成。

基本语法格式:(匿名函数)(参数) 

直接上个栗子吧:

(lambda x:print(x*x))(3)  #构建完成,马上调用

 

回调函数: (更新ing

回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。

觉得主函数类似于一个多功能螺丝刀,传递进去的函数就是各种型号的螺丝刀头,根据实际需要组合起来能实现一定的功能。好像是这么个东东,后期在更新吧。

来一个根据理解搞得栗子:

def func_1():
    print("我是函数1")
def func_2():
    print("我是函数2")
def func_3():
    print("我是函数3")
def func_4():
    print("我是函数4")


def func_0(f):
    print("我是函数0")
    f()
func_0(func_1)
func_0(func_4)
func_0(func_2)

 

回调函数参考链接:https://www.zhihu.com/question/19801131/answer/27459821

 

常用内置函数

 

常见函数

备注功能简介

abs()

返回绝对值

all()

如果可遍历对象中,元素全都可以通过bool判断,就为true

any()

遍历对象中,任一元素能通过bool判断,返回true

ascii()

返回unicode字节码







bin()

将十进制转化为

bool()

判断数据结构为truefalse。空或者0为false

bytearray()


bytes()


callable


chr()


complex()


credits()


delattr


dir()


dict()

创建一个字典

divmod()


enumerate()

返回的是一个枚举对象。常和遍历循环结合使用

eval()

把字符串中包含的特殊字符(列表、字典等)转化为原有数据类型

exec()

把字符串类型的代码直接执行。并按原有语句进行输出

filter()

按条件进行筛选,对列表、集合等进行筛选

float()

将数据转化为浮点型

frozenset()

返回一个冻结的集合对象

getattr()


globals()


hasattr()


hash()

对传入的对象进行hash,哈希的对象必须是不可变的。

hex()

将十进制数值转化为十六进制形式

id()

返回对象的内存地址

input()

接收用户输入,常用语交互

int()

截取数据整形

isinstance()

判断数据结构的具体数据类型

issubclass()


iter()


len()

获取对象的长度

list()

将对象拆开,作为元素放入新建列表

locals()

查看局部变量

map()

用于构建组合列表,常和lambda组合使用

max()

最大值

memoryview()


min()

最小值

next()

在迭代器中,取出对象。 __next__()

object()


oct()


open()

打开文件,并返回操作句柄

ord()


print()

打印输出

property()


quit()


range()

返回按参数给定的范围

repr()

 

reverse()

列表等序列翻转

round()

四舍五入

set()


setattr()

 

slice()

常作用于遍历对象,设置起止索引和步长

sorted()

排序

str()

返回对象的字符串形式,作用类似于给操作对象加引号 ” “

sum()


super()

常用于类的继承,根据mro列表,构建上一级类(不一定是当前类的父类)的对象.在pyt2中需要传参数super(Class, self).***  ,在py3中参数可以传也可以省略,super().***

vars()


zip()

把2个以上的列表拼成一个,作用类似于extend

 

 函数部分示例

 1 # !/usr/bin/env python
 2 # -*- coding:utf-8 -*-
 3 
 4 # ----------函数、参数作用域------------
 5 # def func1(a,b,c='sss',d='cnn'):
 6 #     print(a,b,c,d)
 7 
 8 
 9 # def func2(a,*args,**kwargs):
10 #     print(a,args,kwargs)
11 
12 
13 # -------嵌套函数------
14 # func2(111,'sss',2222222,name="zhangsan")
15 
16 # name = 1
17 #
18 #
19 # def func3():
20 #     name = 2
21 #
22 #     def func31():
23 #         print(name)
24 #         return func31
25 #     return func31
26 # name =3
27 # func3()()()
28 
29 
30 # ----------高阶函数----------
31 # def divs(a,b):
32 #     return float(a)/float(b)
33 #
34 #
35 # def rms(num1,num2,rm=2,f=divs):
36 #     resault = f(num1,num2)
37 #     return resault.__round__(rm)
38 #
39 #
40 # print(rms(51230328238,8126264,4))
41 
42 
43 # -----------递归函数---------------
44 
45 # def func_half(n):
46 #     print(n)
47 #     if n > 0:
48 #         n = int(n/2)
49 #         func_half(n)
50 #     print(n)
51 #
52 #
53 # func_half(1000)
54 
55 
56 def func_find(root_list,num):
57     if len(root_list)>1:
58         if root_list[int(len(root_list)/2)] == num:
59             print("found it")
60         else:
61             root_list = root_list[int(len(root_list)/2):] if root_list[int(len(root_list)/2)] < num else root_list[:int(len(root_list)/2)]
62             func_find(root_list,num)
63     elif root_list[0] == num:
64         print('found it finally')
65     else:
66         print('not in')
67 
68 
69 list1 = [i for i in range(100)]
70 
71 # func_find(list1, 20)
72 
73 print(globals())
74 print(locals())
基本函数定义、高阶函数、递归函数

 

 

 

 

posted @ 2019-04-25 00:07  林山风火  阅读(30)  评论(0)    收藏  举报