函数/匿名函数
函数的定义
def func(): print(1) print(2) print(3) return print(4) func() 结果: 1 2 3 4
定义:def 关键字开头,空格之后接函数名和圆括号(),最后为冒号:,def 是固定的不能改变,空格将def关键字和函数名分开,函数名的命名规则和变量一样
调用:就是 函数名()
rerutn : 当函数遇到return是,函数结束执行
函数的执行流程
函数只有在调用时才会执行,由上到下逐行执行
函数的返回值
return 关键字 ,谁调用就返回给谁
def func(): s = 1+2 print(s) sum1 = func() print(sum1)
不写retnrn时,会默认返回一个None
def func(): s = 1+2 print(s) sum1 = func() print(sum1) 结果: 3 None
只写return,后面不写其他内容,也会返回None
def func(): s = 1+2 print(s) return sum1 = func() print(sum1) 结果: 3 None
写return和不写return的区别,当函数中遇到return时,此函数结束,不会在继续执行
def func(): s = 1+2 return print(s) #print(s)并没有执行 sum1 = func() print(sum1) 结果: None
返回一个值,返回的是值,而不是某个变量,返回的数据类型是其本身的数据类型
def func(): s = 1+2 return s,1,2,['a','b'] sum1 = func() print(sum1) 结果: 3
返回多个值
def func(): s = 1+2 return s,1,2,['a','b'] sum1 = func() print(sum1) 结果: (3, 1, 2, ['a', 'b'])
返回的多个值会被组织成一个元组被返回,也可以用多个值来接受
def func(): return 1,2,['q','w'],3 a,b,c,d = func() print(a,b,c,d) 结果: 1 2 ['q', 'w'] 3
注意:return和返回值之间要有空格,可以返回任意数据类型的值
函数的参数
参数:也就是函数括号里的内容 函数在调用的时候指定一个具体的变量的值 就是参数
def func(n): #形参 print("hello"+n) func("world") #实参 #将wolrd传入到n的过程叫传参
传入多个参数,参数之间用逗号分隔
def func(x,y): max1 = x if x > y else y return max1 n = func(3,4) print(n)
位置参数
按照位置传值,实参的角度
def func(x,y): # 此时 x=3 y=4 max1 = x if x > y else y return max1 n = func(3,4) print(n)
按照关键字传值
def func(x,y): # 此时 x=4 y=3 max1 = x if x > y else y return max1 n = func(y=3,x=4) print(n)
混合传参
def func(x,y): # 此时 x=4 y=3 max1 = x if x > y else y return max1 n = func(4,y=3) print(n)
注意:位置参数必须在关键字参数前面,对于一个形参只能赋值一次
默认参数
将变化比较小的值设置为默认参数
默认参数
def func(name,state = "CN"): print(name,state) func('wanglan')
想要修改默认参数,传值即可
def func(name,state = "CN"): print(name,state) func('wanglan','USA')
默认参数陷阱
def func(a,l=[]): # 当函数执行时,发现有默认参数,会在内存中生成一个列表,l=[]只会创建一次,并不会多次创建,在函数内部使用时相当于全局变量,只是其它函数无法使用 l.append(a) print(l) func(1) #当函数调用时,会在列表中添加1 func(2) #第二次调用,会继续添加 func(3) #第三次调用,继续添加 结果: [1] [1, 2] [1, 2, 3]
def func(a,l=[]): #只要是默认参数是一个可变数据类型,通过默认参数对其修改,修改的就是同一个 l.append(a) print(l) func(1) #使用默认参数修改 func(2,[]) #不在使用默认参数,传入一个空列表 func(3) #使用默认参数修改 结果: [1] [2] [1, 3]
动态位置参数
按位置传值多余的参数由args统一接受,保存成一个元组的形式
def func(*args): print(args) func('Hello','world','wang','lan') 结果: ('Hello', 'world', 'wang', 'lan') #收到的结果是一个元组
注意:动态参数要写在位置参数后面
动态默认参数
按默认传值多余的参数由kwargs统一接受,保存成一个字典的形式
def func(**kwargs): print(kwargs) func(a = 'Hello',b = 'world',c = 'wang',d = 'lan') 结果: {'a': 'Hello', 'b': 'world', 'c': 'wang', 'd': 'lan'} #收到的结果是一个字典
最终顺序: 位置参数 > *args(动态位置参数) > 默认值参数 > **kwargs(动态默认参数)
其他传参方式
接受所有的参数
def func(*args,**kwargs): print(args,kwargs) func(1,23,5,a=1,b=6)
动态参数其他传参
st = [1,4,7] # 方法一 def func(*args): print(args) func(lst[0],lst[1],lst[2]) # 方法二 def func(*args):# 在形参的位置上用*把收到的参数组合成一个元祖 print(args) func(*lst) #在实参的位置上用*将lst(可迭代对象)按照顺序打散
字典也可以进行打散,不过需要**
def func(**kwargs): print(kwargs) #返回字典 func(**dic) def func(**kwargs): print(*kwargs) #加上*返回的就是字典的key func(**dic)
命名空间和作用域
在python解释器开始执行之后, 就会在内存中开辟一个空间, 每当遇到一个变量的时候, 就把变量名和值之间的关系记录下来, 但是当遇到函数定义的时候, 解释器只是把函数名读入内存, 表示这个函数存在了, 至于函数内部的变量和逻辑, 解释器是不关心的. 也就是说一开始的时候函数只是加载进来, 仅此而已, 只有当函数被调用和访问的时候, 解释器才会根据函数内部声明的变量来进行开辟变量的内部空间. 随着函数执行完毕, 这些函数内部变量占用的空间也会随着函数执行完毕而被清空
def fun(): a = 10 print(a) fun() print(a) # a不不存在了了已经 结果: NameError: name 'a' is not defined
命名空间:
内置命名空间 :作用域 全局和局部,加载是在运行之后,代码之前
全局命名空间 :作用域 全局和局部,加载是在运行代码的时候
局部命名空间 :作用域 在局部,加载是在调用的时候
加载顺序:
内置命名空间(程序运行前加载)->全局命名空间(程序运行中:从上到下加载)->局部命名空间(程序运行中:调用时才加载
取值 :
在局部调用:局部命名空间->全局命名空间->内置命名空间
x = 1 def func(): x = 2 #局部存在使用局部 print(x) func() 结果 2 x = 1 def func(): x = 2 def func1(): #不存在调用父级,找到就使用,找不到就使用全局 print(x) func1() func() 结果 2
覆盖 :
同一个名字 : 局部覆盖全局和内置,全局覆盖内置
永远不要起和你知道的内置命名空间中重名的名字
可以通过globals()函数来查看全局作用域中的内容, 也可以通过locals()来查看局部作用域中的变量量和函数信息
def func(): a = 40 b = 20 print("哈哈") print(a, b) # 这里使用的是局部作用域 print(globals()) # 打印全局作用域中的内容 print(locals()) # 打印局部作用域中的内容 func()
函数的嵌套
def f1(): def f2(): def f3(): print("in f3") print("in f2") f3() print("in f1") f2() f1()
def max2(x,y): m = x if x>y else y return m def max4(a,b): res1 = max2(a,b) #调用max2函数 return res1 n = max4(6,2) print(n)
global和nonlocal
global:在局部改全局
a = 10 def func(): global a # 加了个global表示将局部变量改为全局变量 a = 20 print(a) func() print(a)
nonlocal:在小局部改大局部,两个局部之间一定有嵌套关系(修改父级变量),如果父级没有继续向上找,直到函数的第一层,如果还没有就保错,nonlocal不能修改全局变量
def f1(): a = 1 def f2(): nonlocal a #更改父级变量 a = 2 #将父级变量的a=1 修改为a =2 f2() print('a in f1 : ',a) f1()
函数名的本质
1. 函数名可以被引用
def func(): print('in func') f = func print(f)
2函数名可以被当作容器类型的元素
def f1(): print('f1') def f2(): print('f2') def f3(): print('f3') l = [f1,f2,f3] d = {'f1':f1,'f2':f2,'f3':f3} #调用 l[0]() d['f2']()
3.函数名可以当作函数的参数和返回值,就是可以当普通变量用
def login_failed(): print('登陆失败') def index(): print('欢迎来到首页') def login(usr,pwd): if usr == 'wanglan' and pwd == '123': return index else: return login_failed res_func = login('wanglan','123') res_func()
4.函数名可以作为函数的参数
def login(): print('欢迎登陆') def index(auth): auth() print('欢迎来到首页') index(login)
闭包
内部函数使用了外部函数的变量,内部函数就是一个闭包
def func(): name = 'wanglan' def inner(): print(name)
闭包常用的用法
def func(): name = 'wanglan' def inner(): print(name) return inner f = func() f()
可以通过__closure__ 来查看是不是闭包,有值就是,没值就不是
def func(): name = 'wanglan' def inner(): print(name) print(inner.__closure__) return inner f = func() f()
闭包嵌套
def wrapper(): #1加载 money = 1000 def func(): #3.加载 name = 'wanglan' def inner(): #6.加载 print(name,money) #9.打印 return inner #7.返回内存地址 return func #4.返回内存地址 f = wrapper() #2.调用 i = f() #5.调用 i() #8.调用
匿名函数
匿名函数:为了解决那些功能很简单的需求而设计的一句话函数
def calc(n): return n ** n print(calc(10)) # 换成匿名函数 calc = lambda n: n ** n #lambda:关键字 n:形参 n ** n :返回值,就是return后面的内容 print(calc(10))
小结
函数的定义规则
1.定义:def 关键词开头,空格之后接函数名称和圆括号()。 2.参数:圆括号用来接收参数。若传入多个参数,参数之间用逗号分割。 参数可以定义多个,也可以不定义。 参数有很多种,如果涉及到多种参数的定义,应始终遵循位置参数、*args、默认参数、**kwargs顺序定义。 如上述定义过程中某参数类型缺省,其他参数依旧遵循上述排序 3.注释:函数的第一行语句应该添加注释。 4.函数体:函数内容以冒号起始,并且缩进。 5.返回值:return [表达式] 结束函数。不带表达式的return相当于返回 None def 函数名(参数1,参数2,*args,默认参数,**kwargs): """注释:函数功能和参数说明""" 函数体 …… return 返回值
函数的调用规则
1.函数名() 函数名后面+圆括号就是函数的调用。 2.参数: 圆括号用来接收参数。 若传入多个参数: 应按先位置传值,再按关键字传值 具体的传入顺序应按照函数定义的参数情况而定 3.返回值 如果函数有返回值,还应该定义“变量”接收返回值 如果返回值有多个,也可以用多个变量来接收,变量数应和返回值数目一致 无返回值的情况: 函数名() 有返回值的情况: 变量 = 函数名() 多个变量接收多返回值: 变量1,变量2,... = 函数名()
命名空间
一共有三种命名空间从大范围到小范围的顺序:内置命名空间、全局命名空间、局部命名空间
小范围的可以用大范围的
但是大范围的不能用小范围的
范围从大到小(图)
在小范围内,如果要用一个变量,是当前这个小范围有的,就用自己的
如果在小范围内没有,就用上一级的,上一级没有就用上上一级的,以此类推。
如果都没有,报错
函数的嵌套:
嵌套调用
嵌套定义:定义在内部的函数无法直接在全局被调用
函数名的本质:
就是一个变量,保存了函数所在的内存地址
闭包:
内部函数包含对外部作用域而非全剧作用域名字的引用,该内部函数称为闭包函数