一 函数
函数是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段。
函数能提高应用的模块性,和代码的重复利用率。
函数需要先定义再使用
二 函数类别
Python提供了许多 内置函数,比如print(),len()....
#内置函数:内置到解释器中 >>> len <built-in function len> >>> print <built-in function print>
但你也可以自己创建函数,这被叫做 用户自定义函数。
定义函数的格式:
1 ''' 2 def 函数名(arg1,arg2,arg3): 3 '描述信息' 4 函数体 5 return 6 '''
1 def foo(): 2 print('from the foo') 3 4 5 def bar(x,y): 6 print('from bar') 7 res=x+y 8 return res 9 10 11 # foo() #函数调用的语句形式 12 # res=bar(1,2) #函数调用的表达式形式 13 # res1=bar(1,2)*10 #函数调用的表达式形式 14 # print(res1) 15 16 17 res2=bar(bar(1,2),3) #函数的调用作为另外一个函数的参数 18 19 print(res2)
- 函数代码块以 def 关键词开头,后接函数标识符名称和圆括号 ()
- 任何传入参数和自变量必须放在圆括号中间,圆括号之间可以用于定义参数。
- 函数的第一行语句可以选择性地使用文档字符串—用于存放函数说明。
- 函数内容以冒号起始,并且缩进。
- return [表达式] 结束函数,选择性地返回一个值给调用方。不带表达式的return相当于返回 None。
1 #用函数输出以下内容: 2 ''' 3 ############# 4 落霞与孤鹜齐飞 5 ############# 6 ''' 7 8 def print_line(): 9 print('#'*13) 10 11 def print_msg(): 12 print('落霞与孤鹜齐飞') 13 14 def print_line(): 15 print('#'*13) 16 17 print_line() 18 print_msg() 19 print_line()
三 函数的参数
A.
#1、位置参数:按照从左到右的顺序定义的参数 位置形参:必选参数 位置实参:按照位置给形参传值 #2、关键字参数:按照key=value的形式定义的实参 无需按照位置为形参传值 注意的问题: 1. 关键字实参必须在位置实参右面 2. 对同一个形参不能重复传值 #3、默认参数:形参在定义时就已经为其赋值 可以传值也可以不传值,经常需要变得参数定义成位置形参,变化较小的参数定义成默认参数(形参) 注意的问题: 1. 只在定义时赋值一次 2. 默认参数的定义应该在位置形参右面 3. 默认参数通常应该定义成不可变类型 #4、可变长参数: 可变长指的是实参值的个数不固定 而实参有按位置和按关键字两种形式定义,针对这两种形式的可变长,形参对应有两种解决方案来完整地存放它们,分别是*args,**kwargs ===========*args=========== def foo(x,y,*args): print(x,y) print(args) foo(1,2,3,4,5) def foo(x,y,*args): print(x,y) print(args) foo(1,2,*[3,4,5]) def foo(x,y,z): print(x,y,z) foo(*[1,2,3]) ===========**kwargs=========== def foo(x,y,**kwargs): print(x,y) print(kwargs) foo(1,y=2,a=1,b=2,c=3) def foo(x,y,**kwargs): print(x,y) print(kwargs) foo(1,y=2,**{'a':1,'b':2,'c':3}) def foo(x,y,z): print(x,y,z) foo(**{'z':1,'x':2,'y':3}) ===========*args+**kwargs=========== def foo(x,y): print(x,y) def wrapper(*args,**kwargs): print('====>') foo(*args,**kwargs) #5、命名关键字参数:*后定义的参数,必须被传值(有默认值的除外),且必须按照关键字实参的形式传递 可以保证,传入的参数中一定包含某些关键字 def foo(x,y,*args,a=1,b,**kwargs): print(x,y) print(args) print(a) print(b) print(kwargs) foo(1,2,3,4,5,b=3,c=4,d=5) 结果: 1 2 (3, 4, 5) 1 3 {'c': 4, 'd': 5}
1.形参变量只有在被调用时才分配内存单元,在调用结束时,即刻释放所分配的内存单元。因此,形参只在函数内部有效。函数调用结束返回主调用函数后则不能再使用该形参变量
def foo(x,y): print(x) print(y) foo(1,2) # foo(y=2,x=1) #只是与上面的 输入方式不同,结果同
2.实参可以是常量、变量、表达式、函数等,无论实参是何种类型的量,在进行函数调用时,它们都必须有确定的值,以便把这些值传送给形参。因此应预先用赋值,输入等办法使参数获得确定值
3.位置参数和关键字(标准调用:实参与形参位置一一对应;关键字调用:位置无需固定)
1 #------在实参的角度------ 2 3 def foo(x,y): 4 print(x) 5 print(y) 6 7 # 第一种:按照位置传值 8 foo(1,2) 9 foo(2,1) 10 11 # 12 # 第二种:按照关键字传值 13 foo(x=1,y=2) 14 foo(y=2,x=1) 15 16 # 第三种:混着用 17 foo(1,y=2) 18 # 问题一:按位置传值必须在按关键字传值的前面 19 foo(y=2,1) #报错 20 # 问题一:对于一个形参只能赋值一次 21 foo(1,y=2)
4.默认参数
1 #默认参数:变化比较小的,经常使用。不用传值(非传也行) 2 3 def foo(x,y=1): # y=1 为 默认参数 4 print(x) 5 print(y) 6 7 foo(1) 8 9 #结果 10 1 11 1
默认参数必须注意的问题是:默认参数必须放到位置参数的后面
5.参数组
B. 有参函数与无参函数的角度
无参函数:通常 不需有 返回值。它内部通常只是些语句
有参函数:通常要 有返回值
1 def foo(): #无参函数 2 print('from the foo') 3 4 def bar(x,y): #有参函数 5 print('from bar') 6 res = x+y 7 return res #相当于 return 3 8 9 bar(1,2) 10 n = bar(1,2) #n=3,此处的 n,相当于 res所代表的 3 11 print(n)
四 *args 与 **kwargs的使用(其实是 *与** 的使用)
1.*args
1 #例一 2 # *args 3 def foo(x,*args): 4 print(x) 5 print(args) 6 7 foo(1,2,3,4,5) 8 9 #输出结果 10 1 11 (2, 3, 4, 5) 12 13 14 ##例二: *args与位置参数和默认参数的混用 15 def foo(x,*args,y=1): 16 print(x) 17 print(args) 18 print(y) 19 20 foo(1,2,3,4,5,y=333333) 21 22 # #输出结果 23 1 24 (2, 3, 4, 5) 25 333333
2.**kwargs
1 # **kwargs 2 def foo(x,*args,**kwargs): 3 print(x) 4 print(args) 5 print(kwargs) 6 7 foo(1,y=1,z=2) 8 9 #输出结果 10 1 11 () #元组形式 12 {'y': 1, 'z': 2}
五 return语句
return [表达式] 语句用于退出函数,选择性地向调用方返回一个表达式。
1.没有 return
1 def foo(): 2 print('from the foo') 3 4 res = foo() 5 print(res) 6 7 #返回值 8 from the foo 9 None #None,也是返回值
2.有 return ,返回一个值
1 #有return:返回一个值 2 def my_max(x,y): 3 res = x if x > y else y 4 return res 5 6 res1 = my_max(1,2) 7 print(res1) 8 9 10 #返回结果 11 2
2.有 return ,返回多个值
1 #有return:返多个值 2 3 def bar(x,y): 4 return 333,666,111,[11,22],{'a':3} #可返回多个值,任意类型的值(会以 元组形式出现) 5 6 res2 = bar(1,2) 7 print(res2) 8 9 #返回结果 10 11 (333, 666, 111, [11, 22], {'a': 3}) #元组
六 嵌套函数和作用域
1.嵌套定义
1 #####例一 2 3 x = 33333333333333 4 def f1(): 5 x = 1 6 print('------>f1 ',x) 7 def f2(): 8 x=2 9 print('---->f2 ',x) 10 def f3(): 11 x=3 12 print('-->f3 ',x) 13 f3() 14 f2() 15 f1() 16 17 #运行结果 18 19 ------>f1 1 20 ---->f2 2 21 -->f3 3 22 23 24 25 26 27 ######## 例二 28 29 x = 33333333333333 30 def f1(): 31 #x = 1 #此表达式 选择 不执行 32 print('------>f1 ',x) 33 def f2(): 34 #x=2 #此表达式 选择 不执行 35 print('---->f2 ',x) 36 def f3(): 37 #x=3 #此表达式 选择 不执行 38 print('-->f3 ',x) 39 f3() 40 f2() 41 f1() 42 43 #运行结果 44 ------>f1 33333333333333 45 ---->f2 33333333333333 46 -->f3 33333333333333
2,嵌套调用
1 def my_max(x,y): 2 res=x if x > y else y 3 return res 4 print(my_max(10,100)) 6 7 8 def my_max4(a,b,c,d): 9 res1 = my_max(a,b) 10 res2 = my_max(res1,c) 11 res3 = my_max(res2,d) 12 return res3 13 print(my_max4(1,20,3,4)) 14 15 16 #输出的结果 17 100 #print(my_max(10,100))的结果 18 20 #print(my_max4(1,20,3,4))的结果
七 闭包
一:什么是闭包?
闭包就是函数嵌套以及由此产生的作用域问题。内部函数包含对外部作用域而非全局作用域的引用。
而较官方的解释,闭包(Closure)是词法闭包(Lexical Closure)的简称,是引用了自由变量的函数。这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外。所以,有另一种说法认为闭包是由函数和与其相关的引用环境组合而成的实体。闭包在运行时可以有多个实例,不同的引用环境和相同的函数组合可以产生不同的实例。
一个闭包就是调用了一个函数A,这个函数A返回了一个函数B给你。这个返回的函数B就是闭包。你在调用函数A的时候传递的参数就是自由变量。
举例:
def func(name): def inner_func(age): print('name:{},age:{}'.format(name,age)) return inner_func bb = func('大宝') bb(18) #结果:name:大宝,age:18
这里面调用func的时候,就产生了一个闭包-----inner,并且该闭包持有自由变量----name,因此这也意味着,当函数func的生命周期结束之后,name这个变量依然存在,因为它被闭包引用了,所以不会被回收。
nonlocal语句
在python函数内,可以直接引用外部变量,但不能改写外部变量,因此如果闭包中直接改写父函数的变量,就会发生错误:
def cnt(param): count = 0 def counter(): count += 1 print("I'm:",param,"NO:",str(count)) return counter test = cnt('tom') test() #报错:UnboundLocalError: local variable 'count' referenced before assignment
在Python2中,可以在函数内使用global语句,但全局变量在任何语言中不被提倡,因为它很难控制。Python3中引入了nonlocal语句解决了这个问题:
def cnt(param): count = 0 def counter(): nonlocal count count += 1 print("I'm:",param,"No.",str(count)) return counter test = cnt('tom') test() test() test() #结果: I'm: tom No. 1 I'm: tom No. 2 I'm: tom No. 3
nonlocal 与 global 的区别在于 nonloacl语句,会去搜寻局部变量与全局变量之间的变量,也就是会优先寻找层级关系与闭包作用域最近的外部变量。