一、什么是函数
是可以重复执行的语句块,可以重复调用 |
|
作用 |
用于封装语句块,提高代码的重用性 用于定义/创建用户级别的函数 |
语法 |
def 函数名(形参列表): 语句块 创建一个函数,把函数内的语句块打包为一个函数,用函数名绑定 |
说明 |
1.函数名就是语句块的名称 2.函数名的命名规则与变量名相同(函数名必须是标识符) 3.函数名是一个变量(绑定对象,绑定函数) 4.函数有自己名字空间,在函数外部不可以访问函数内部的变量,在函数内部可以访问函数外部的变量,要让函数处理外部数据需要用参数给函数传入一些数据 5.参数列表可以为空 6.语句部分不能为空,如果为空需要用pass语句填充 |
函数调用 |
函数名(实际调用传递参数) |
说明 |
1.函数调用是一个表达式 2.如果没有return语句,函数执行完毕后返回None对象 3.如果函数需要返回其它的对象需要用到return语句 |
问题 |
|
def mysum(a,b) print(a+b) r = mysum(100,200) print(r) |
#请问r绑定的是什么? >>>会打印出300和none。r绑定的是none(因为没有return,就返回none) |
Def mysum(a,b): Result = a + b R = mysum(100,200) Print(result) |
#打印错误 >>>函数外部无法访问函数内部的局部变量 函数内部可以访问函数外部的变量,但不能修改函数外部变量的绑定关系 |
x = 0 Def myadd(a,b): x = a + b Print(x) Myadd(100,200) Print(x) |
>>>0 >>>第一行的x和函数里面的x不是同一个x,只是同名而已。最后一行print的还是全局变量的x,0;函数里面的x是另外一个x。 |
二、函数的参数
2.1函数的参数传递
将实参传给函数
传递方式 |
函数示例为: Def myfun(a,b,c): Print(a) Print(b) Print(c) |
|
位置传参 |
myfun(A,B, C) |
实际传递参数(‘实参’)与形式参数(‘形参’)的对应关系按位置来依次对应 |
序列传参 |
L = [1.1, 2.2, 3.3] myfun(*L) |
函数调用过程中用‘*’将序列拆解后按位置进行传递 ps:参数个数与序列元素要相等 |
关键字传参 |
myfun(b=2, c=3, a=1) |
传参时,按形参的‘名称’给形参赋值,实参和形参按名称进行匹配 ps:传参时,只能给函数有的关键字,且不能重复 |
字典关键字传参 |
d = {‘a’:111,‘c’:333,‘b’:222} Myfun(**d) 输出结果为 111 222 333 |
实参为字典,将字典用“**”拆解后进行关键字传参的传参方式 |
说明: 1.字典的键名和形参名必须一致; 2.字典的键名必须为字符串(且必须为标识符的规则); 3.字典的键名要在形参中存在 |
||
综合传参 |
在能确定形参能唯一匹配到应当实参的情况下,可以任意组合; 位置传参在前,关键字传参在后。 |
|
myfun(1, c=3, b=2) #正确 Myfun(b=2, a=1, 3) #错误,位置参数要在前 Myfun(100, *[200, 300]) #正确 Myfun( *‘AB’,300 ) #正确 Myfun(*[100], c=300, b=200) #正确,先传给a,是位置传参,后再传给b、c是关键字传参 Myfun(*“AB”,**{“c”:300}) #正确 |
2.2函数的形参定义
接收实参
2.2.1函数的缺省参数
语法 |
def 函数名(形参名1=默认实参1, 形参名2=默认实参2, …) |
说明 |
1.缺省参数必须自右至左依次存在: 如果一个参数有缺省参数,则其右侧的所有参数都必须有缺省参数 def fa(a=1,b,c=3): #是错的 def fb(a,b=10,c): #是错的 2.缺省参数可以有0个或多个,甚至全部都是缺省参数 |
2.2.2 定义方式
位置形参 |
def 函数名(形参名1,形参名2, …): 语句块 |
||
星号元组形参 |
def 函数名(*元组形参名): 语句块 作用:收集多余的位置传参 |
def fb(*args): '''args绑定一个元组''' print("实参的个数是:", len(args)) print("args=", args) fb() fb(1, 2, 3, 4) >>>args = (1, 2, 3, 4) |
|
位置形参和星号元组形参都是接收位置传参。只不过星号元组形参还要接收多余的参数 |
|||
命名关键字形参 |
Def 函数名(*,命名关键字形参) 或 Def 函数名(*args,命名关键字形参) 作用:强制*后面所有的传参都必须使用关键字传参,*相当于分隔符 |
# 此示例示意函数的命名关键字形参 def fa(a, b, *, c, d): '''强制c,d必须用关键字传参 ''' print(a, b, c, d) fa(1, 2, d=400, c=300) # 对的 def fb(a, b, *args, c, d): print(a, b, args, c, d) fb(1, 2, 3, 4, d=400, c=200) # 1,2,(3,4),200,400 fb(1,2,3,4,5, **{'d':400, 'c':300}) #1,2,(3,4,5),300,400 # 问题: # fb(1,2,3,4, c=400, d=300, e=500) # 出错,e是多余的 # fa(1, 2, 3, 4) # 错的 问题: def fb(a, b, *args, c, d): print(a, b, c, d) fb(1, 2, 3, 4, c=400, d=300, e=500)能否传参? |
|
双星号字典形参 |
def 函数名(**字典形参名): 作用:收集多余的关键字传参。只能有一个 |
# 此示例示意双星号字典形参的用法 def fa(**kwargs): '''kwargs绑定字典''' print("多余的关键字传参的个数是:", len(kwargs)) print("kwargs =", kwargs) fa(a=10, b=20, c=30) #{'a':10, 'c':30, 'b':20} def fb(*args, a, **kwargs): print(args, a, kwargs) fb(1,2,3, b=20, c=30, a=10) #(1,2,3) 10 {'b':20,'c':30} |
|
函数的参数说明 |
1.位置形参,星号元组形参,命名关键字形参,双星号字典形参,缺省参数可以混合使用 2.函数参数自左至右的顺序依次为: 位置形参、星号元组形参、命名关键字形参、双星号字典形参 |
|
Def fn(a,b, *args, c,d,**kwargs): pass fn(1,2,3,4,c=100,d=200,e=300,f=400) >>>1给a,2给b,3和4打包成一个元组(*args),c给c,d给d,e和f打包成一个字典(**kwargs) |
||
三、函数的变量
函数名是变量,它在创建时绑定一个函数 |
一个函数可以作为另一个函数实参传递 |
Def f1(): Print(‘f1被调用’) Def f2(): Print(‘f2被调用’) Def fx(fn): Print(‘fn绑定的是:’,fn) Fn() #调用fn绑定的函数 Fx(f1) Fx(f2) |
目前有3个全局变量,f1,f2,fx |
函数作为另一个函数的返回值 |
示例: # 此示例示意get_op这个函数可以返回其它的函数,max/min/sum def get_op(): s = input("请输入您要做的操作: ") if s == '求最大': return max elif s == '求最小': return min elif s == '求和': return sum L = [1, 2, 3, 4] fx = get_op() #fx绑定的是get_op返回的函数max/min/sum print( fx(L) ) #然后调用max/min/sum其中的函数,求L的最大、最小、和 |
函数的嵌套定义 |
|
指一个函数里用def语句来创建其它函数的情况 |
|
示例:function_embed_def.py # 此示例示意函数内部来嵌套定义其它函数 def fun_outer(): print("fun_outer被调用...") # 在此处创建另一个函数,并在下面调用 def fun_inner(): print("fun_inner被调用") fun_inner() # 调用一次 fun_inner() # 调用二次... print("fun_outer调用结束") return fun_inner f = fun_outer() # 调用函数 f() # 来调用 fun_outer里创建的那个fun_inner? # fun_inner() # 调用失败 |
四、函数的返回值
Return语句 |
|
语法 |
Return [ 表达式 ] 注:[ ] 代表内容可略 |
作用 |
结束当前函数的执行,返回到调用该函数的地方,同时返回表达式的引用关系 |
说明 |
1.Return语句后跟的表达式可以省略,省略后相当于return none 2.如果函数内没有return语句,则函数执行完最后一条语句后返回none(相当于在最后加了一条return none语句) |
五、函数式编程
是指一系列函数解决问题 |
|
函数是一等公民(Guido says) |
1.函数本身可以赋值给变量,赋值后变量绑定函数 |
2.允许将函数本身作为参数传入另一个函数 |
|
3.允许函数返回一个函数 |
|
好处 |
1.用每一个函数完成细小的功能,一系列函数在任意组合可以完成大问题 |
函数的可重入性 |
1.当一个函数在运行时不会读取和改变除局部作用域以外的变量时,此函数为可重入函数 |
2.可重入函数在每次调用时,如果参数一定,则结果必然一定 |
|
可重入函数示例 |
Def add1(x,y): Return x+y >>>add1(100,200)调用一万次,结果都一样 |
不可重入函数示例: |
Y = 200 Def add2(x): Return x + y Print(myadd2(10)) #210 Y=300 Print(myadd2(10)) #310 |
六、递归函数 Recursion
函数直接或间接的调用自身 |
||
示例 |
#直接调用自己,进入递归 Def f(): f() f() |
#函数间接调用自身 Def fa(): fb() def fb(): fa() fa() print(“递归完成”) |
说明 |
1.递归一定要控制递归的层数,当符合一定条件时要终止递归调用 |
|
2.几乎所有递归都能用while循环来代替 |
||
优点 |
1.递归可以把问题简单化,让思路更为清晰,代码更简洁 |
|
缺点 |
1.递归因系统环境影响大,当递归深度太大时,可能会得到不可预知的结果 |
递归的两个阶段 |
|
递推阶段 |
从原问题出发,按递归公式递推从未知到已知,最终达到递归的终止条件 |
回归阶段 |
按递归终止条件求出结果,逆向逐步代入递归公式,回归到问题求解 |
递归的实现方法 |
先假设函数已经实现 |
练习 |
写一个函数mysum(n),用递归方法求 |
七、闭包closure
是指引用了此函数外部嵌套函数作用域变量的函数 |
|
闭包必须满足三个条件: |
1.必须有内嵌函数 |
2.内嵌函数必须引用外部函数中的变量 |
|
3.外部函数返回值必须是内嵌函数 |
|
def make_power(y): def fn(x): >>>条件1 return x ** y >>>条件2,局部变量y不会消失,还可以接着用 return fn >>>条件3 pow2 = make_power(2) print("5的平方是:",pow2(5)) pow3 = make_power(3) print('6的立方是:',pow3(6)) def fn(x): >>>条件1 return x ** y >>>条件2,局部变量y不会消失,还可以接着用 return fn >>>条件3 pow2 = make_power(2) print("5的平方是:",pow2(5)) pow3 = make_power(3) print('6的立方是:',pow3(6)) |
八、装饰器 Decorators
问题: |
1.函数名是变量,它绑定一个函数 |
2.函数名 /函数名() 区别: 前者,绑定的是一个变量;后者,调用函数 |
|
装饰器定义 |
是一个函数,用来包装另一个函数或类 |
装饰器作用 |
是在不改变原函数名(或类名)的情况下改变被包装对象的行为 |
函数装饰器 |
指装饰器是一个函数,传入的是一个函数,返回的也是一个函数 |
语法 |
def 装饰器函数名(参数): 语句块 Return 函数对象 @装饰器函数名<换行> def 函数名(形参列表): 语句块 |
示例 |
Mydeco1.py #mydeco1.py # def mydeco(fn): # return None # def myfunc(): # print('函数myfunc被调用') # myfunc = mydeco(myfunc) #myfunc = None # myfunc() #相当于None() #--------------------------------------------- def mydeco(fn): def fx(): print("fx被调用") return fx #myfunc加了mydeco装饰器,等同于在myfunc创建之后调用 #即,myfunc = mydeco(myfunc) #将下面的myfunc传入mydeco,并赋值给myfunc(即,myfunc绑定mydeco的返回值) @mydeco def myfunc(): print('函数myfunc被调用') #myfunc绑定的是mydeco返回的fx, myfunc=fx; #这样的写法可以用装饰器来代替 # myfunc = mydeco(myfunc) myfunc() #加了@mydeco >>> fx被调用 #不加@mydeco >>> myfunc被调用 Mydeco2.py def mydeco(fn): def fx(): print("+++++++++++这是myfunc被调用之前++++++++++") #要想在此处调用被装饰的函数myfunc,怎么办? fn() #用fn调用被装饰函数,因为在传参的时候fn = myfunc #不用myfunc,因为已被赋与了mydeco的返回值 #调用fn,形成一个闭包,不会释放myfunc print("+++++++++++这是myfunc被调用之后++++++++++") return fx @mydeco def myfunc(): print('函数myfunc被调用') myfunc() Test_deco.py def mydeco(fn): print("装饰器函数被调用了...") def fx(): print('fx被调用了') return fx # @mydeco #加 不加装饰器结果一样吗?? def myfunc(): print('func被调用了') myfunc() myfunc() #调用第二次 myfunc() #调用第三次 #加装饰器 打印结果 # 装饰器函数被调用了... # fx被调用了 # myfunc = mydeco(myfunc) # fx被调用了 # myfunc()相当于fx(),所以没有'装饰器函数被调用了...' # fx被调用了 #不加装饰器 打印结果 # fx被调用了 # fx被调用了 # fx被调用了 Mydeco4.py #示范 装饰器在不改变原函数和调用者行为的情况下 # 来改变原有函数功能 #装饰器函数 def privillage_check(fn): def fx(name, x): print('正在检查权限...') fn(name, x) #权限通过,则可以调用相应函数 return fx #写一个操作数据的函数(存钱操作) @privillage_check def savemoney(name, x): print(name,'存钱',x,'元') @privillage_check def withdraw(name, x): print(name,'取钱',x,'元') #---------------------以下是调用者 savemoney('小张',200) savemoney('小赵',400) withdraw('小李',500) Mydeco5.py #示范 再加一个装饰器来添加余额变动提醒功能 def send_message(fn): def fy(name, x): fn(name, x) #先办理业务,再发送余额提醒 print('发短信给',name, '办理了',x,'元') return fy #装饰器函数 def privillage_check(fn): def fx(name, x): print('正在检查权限...') fn(name, x) #权限通过,则可以调用相应函数 return fx #send_message修饰的是fx #因为privillage_check返回的是fx @send_message @privillage_check def savemoney(name, x): print(name,'存钱',x,'元') @send_message @privillage_check def withdraw(name, x): print(name,'取钱',x,'元') #---------------------以下是调用者 savemoney('小张',200) savemoney('小赵',400) withdraw('小李',500) |
九、函数的文档字符串
9.1 文档字符串
函数内第一次未赋值给任何变量的字符串是此函数的文档字符串 |
|
语法 |
def 函数名(形参列表): ’’’函数的文档字符串’’’ 函数语句块 |
示例 |
def hello(): ’’’此函数用来打招呼。。。’’’ Pass >>>help(hello) |
说明 |
1.文档字符串通常用来说明本函数的功能和使用方法 |
2.在交互模式下,输入:help(函数名) 可以查看函数的文档字符串 |
|
不是注释,它要占用运行空间(有变量来绑定它),并且也要编译执行 |
9.2__doc__属性
用于记录函数的文档字符串 |
|
交互模式下,输入 函数名 . __doc__ |
9.3 __name__属性
用于记录函数的名称 |
|
Def hello(): Pass f = hello f.__name__ #交互模式下,求f绑定的函数的名字 # >>>‘hello’ Del hello #删除变量 f.__name__ # >>>‘hello’ #del只是删除hello变量,实际的hello()还在 |
十、函数总结
定义语法 |
@装饰器1 @装饰器2 。。。 Def 函数名(位置形参, *元组形参(或*), 命名关键字形参, **字典形参): ’’’文档字符串’’’ 语句块 |
思考题:test.py L = [1, 2, 3] def f(n=0, lst=[]): lst.append(n) print(lst) f(4, L) #[1, 2, 3, 4] f(5, L) #[1, 2, 3, 4, 5] f(100) #[100] # print('123:',lst)# 打印不出来,外面访问不到函数里面的lst f(200) #[100,200],f的缺省参数lst会一直存在 #--------------------------------------------------- L = [1, 2, 3] def f(n=0, lst=None): #*****modified****** if lst is None: #官方给的solution lst = [] lst.append(n) print(lst) f(4, L) #[1, 2, 3, 4] f(5, L) f(100) #[100] f(200) #[200] |
|
附注:
一、局部变量和全局变量
1.1局部变量
1.是定义在函数内部的变量(函数的形参也是局部变量) |
2.局部变量只能在函数内部使用 |
3.局部变量在函数调用时,才能够被创建;在函数调用结束时,会自动销毁;如果还有其它变量绑定,则不会销毁。示例:function_embed_def2.py # 此示例示意函数内部来嵌套定义其它函数 def fun_outer(): print("fun_outer被调用...") # 在此处创建另一个函数,并在下面调用 def fun_inner(): print("fun_inner被调用") fun_inner() # 调用一次 fun_inner() # 调用二次... print("fun_outer调用结束") return fun_inner f = fun_outer() # 调用函数 f() # 来调用 fun_outer里创建的那个fun_inner? # fun_inner() # 调用失败 (闭包closure除外) |
说明: |
1.在函数内首次对变量赋值是创建局部变量,再次为变量赋值是修改局部变量的绑定关系 |
2.在函数内部的赋值语句不会对全局变量造成影响 |
3.局部变量只能在其被声明的函数内部访问,而全局变量可以在整个模块被访问 |
1.2全局变量
1.是定义在函数外部,模块内部的变量 |
2.所有函数都可以直接访问全局变量,但函数内部不能直接通过赋值语句来改变全局变量 |
二、python的作用域
2.1 四个作用域
也叫名字/命名空间(name space),是访问变量时查找变量名的范围空间 |
||
四个作用域:LEGB |
||
作用域 |
英文解释 |
英文简写 |
局部作用域(函数内) |
Local(function) |
L |
外部嵌套函数作用域 |
Enclosing function locals |
E |
函数定义所在模块(文件)的作用域 |
Globals(module) |
G |
Python内置模块的作用域 |
Builtin(python) |
B |
2.2 变量名的查找规则
1.在访问变量时先查找本地变量,然后是包裹此函数外部的函数内部的变量,之后是全局变量,最后是内置变量(L---->E ---->G---->B) |
2.在默认情况下,变量名赋值会创建或者改变本地作用域变量 |
2.3 Global语句
作用 |
1.告诉解释器,global语句声明的一个或多个变量,这些变量的作用域为模块级的作用域(也称作全局变量) |
2.全局声明(global)将赋值的变量映射到模块文件内部的作用域 |
|
语法 |
Global 变量1,变量2, … |
示例 |
Global v, a, b, c |
Global d |
|
global说明 |
1.全局变量如果要在函数内部被赋值,则必须经过全局声明(否则会被认为是局部变量) |
2.全局变量在函数内部不经过声明就可以直接访问 “访问”:获取绑定关系,而不是改变变量 |
|
3.不能先创建局部变量,再用global声明为全局变量,此做法不符合规则 |
|
4.global变量列表里的变量名不能出现在此作用域内形参列表里 (不能既是参数、又是全局变量) |
2.4 nonlocal语句
作用 |
告诉解释器,nonlocal声明的变量不是局部变量,也不是全局变量,而是外部嵌套函数内的变量 |
语法 |
Nonlocal 变量名1,变量名2,… |
说明 |
1.nonlocal语句只能在被嵌套的函数内部进行使用 |
2.访问nonlocal变量将对外部嵌套函数作用域内的变量进行操作 |
|
3.当有两层或两层以上函数嵌套时,访问nonlocal变量只对最近一层的变量进行操作 |
|
4.nonlocal语句的变量列表里的变量名,不能出现在此函数参数列表中 |