闭包(closure)
闭包是python的一个非常有趣有用的设施, 因为有区别于简易plain逻辑,所以比较绕.
下面,我给大家上几盘菜,把我的建立理解的过程,展现给大家.
定义:外部函数里面有一个内部函数,内部函数使用到了外部函数的变量,并且外部函数将这个内部函数作为返回值返回,那么这个内部函数我们称之为闭包.
注意1:内部函数作为返回值返回( 返回值是函数对象,是不带()的 ) .
注意2:函数的嵌套.调用外部函数时,通过外部函数的return,进入内部函数.
一般情况下,在我们认知当中,如果一个函数结束,函数的内部所有东西都会释放掉,还给内存,局部变量都会消失。但是闭包是一种特殊情况,如果外函数在结束的时候发现有自己的临时变量将来会在内部函数中用到,
就把这个临时变量绑定给了内部函数,之后,外部函数结束。
运用函数,一定要考虑参数和变量, 有无参数和变量?参数和变量有无变化?参数和变量是否进行内外传递?
所以,当闭包遇到参数,注定是一场有趣的旅行.
举个小菜 1:
(外部函数无参, 内部函数无参)
1 def hello(): 2 def world(): 3 print('这是world函数') 4 return world 5 6 a = hello() 7 a()
>>>这是world函数
举个小菜 2:
(外部函数有参数, 内部函数无参数)
1 def hello(*args): 2 a = 1 3 def world(): 4 sum = 0 5 for x in args: 6 sum += x 7 return sum 8 return world 9 10 a = hello(100, 200, 300) 11 print(a())
>>>600
内部函数引用了外部函数的参数.
举个小菜 3:
注意:外部函数有参数;
内部函数也有参数,
内部函数引用外部函数中的变量.
1 # 写一个闭包,让你生成不同的二元一次方程, y = a*x+b 2 def hello(a, b): 3 def func(x): 4 return a*x + b 5 return func 6 7 # 外部函数内部,使用闭包(含有参数),使不同功能(函数)的参数进行分离. 8 func1 = hello(3, 2) # 就是3x+2 9 func2 = hello(5, 3) # 就是5x+3 10 11 12 # 闭包使用了外部函数的参数(变量),自己也有形参(变量) 13 print(func1(1))
>>>5 14 print(func2(2))
>>>13 15 print(hello(10, 2)(3))
>>>32
嗯...觉得还好理解,很有意思吧.
下面,给大家一道特色菜,请坐稳啊~
举个特色菜1:
(注意1:内部函数放进列表,被return.)
1 def hello(): 2 fns = [] 3 for x in range(1, 4): 4 def pingfang(): 5 return x*x 6 fns.append(pingfang) 7 return fns 8 9 f1, f2, f3, = hello() 10 11 print(f1()) 12 print(f2()) 13 print(f3())
大家先目测,心算一下结果如何?
>>>1 >>>4 >>>9 ??
正确答案是:
>>>9 >>>9 >>>9 !!
这是为什么呢?
# 关键在于在调用f1() f2() f3()时, 变量x的值是怎样的!
请大家看我的注释代码:
(注意2:外部函数无参,有变量;
内部函数无参,引用外部函数中的变量.)
1 # 闭包特例,内部函数放进列表返回. 2 def hello(): 3 fns = [] 4 for x in range(1, 6): 5 def pingfang(): 6 return x*x 7 fns.append(pingfang) 8 return fns 9 # 把函数pingfang对象存放在列表中 10 # 看下结果.是个列表,内是函数对象. 11 # print(hello()) #>>>[<function hello.<locals>.pingfang at 0x0000022312F2DF28>, <function hello.<locals>.pingfang at 0x0000022312F350D0>, <function hello.<locals>.pingfang at 0x0000022312F351E0>] 12 # 注意这里的函数对象是!!! [<function hello.<locals>.pingfang ...] 13 f1, f2, f3= hello() 14 # 此时进行拆包(包指的是,列表/字典/元组) 15 # 拆包时,先hello(),拆包将列表内的元素,赋值给f1, f2, f3. 16 17 print(f1()) 18 print(f2()) 19 print(f3()) 20 21 # 请注意,pingfang函数无参,但引用外部函数的变量. 22 # 关键在于在调用f1() f2() f3()时,变量x的值是怎样的呢? 23 # 之前,先有f1, f2, f3= hello()了,此时x保存的是range结构跑完之后的x=5.之后,拆给f1, f2, f3,因为闭包引用外部的变量x,所以每次调用都是x=5.所以,每次print都是9. 24 # 看明白了吗?写博客好费时间啊..
此时,多说一句:
闭包的执行是延迟执行,所以要注意使用的外部函数的变量是否还是之前你认为的那个值,因为它很有可能改变.
额...因为存在''外部函数-内部函数-各自参数/变量''的模式,
闭包的故事还没完啊...
再举个特色菜2:
(外部函数无参,有变量
内部函数有参,有变量)
请复制代码,验证结果.
1 def hello(): 2 fns = [] 3 for x in range(1, 4): 4 def pingfang(y=x): 5 return y*y 6 7 fns.append(pingfang) 8 return fns 9 10 f1, f2, f3= hello() 11 12 print(f1()) 13 print(f2()) 14 print(f3()) 15 16 >>>1 17 >>>4 18 >>>9 19 20 # 注意,本次pingfang带有形参,且pingfang函数的return y*y,每次不同. 21 # return pingfang函数组成的列表,而每一次append的都是不同的函数对象,故每次调用-->当次的return y*y
enm,yummy,yummy!
因为难嚼难理解,所以看起来,好像很好吃的样子啊...那好吧,再来一道~
再举个特色菜3:
(外部函数无参,有变量
内部函数有参,有变量,并再嵌套一层有参的函数)
关键在于函数的传参路径,变量的引用路径.
请复制代码,验证结果.
1 def hello(): 2 fns = [] 3 for x in range(1, 4): 4 def pingfang(y): 5 def haha(): 6 return y*y 7 return haha 8 fns.append(pingfang(x)) 9 # fns.append(pingfang) 10 # TypeError: pingfang() missing 1 required positional argument: 'y' 11 return fns 12 13 # print(hello()) 14 # 同样是把函数pingfang对象存放在列表中,内是函数对象. 15 # >>>[<function hello.<locals>.pingfang.<locals>.haha at 0x0000016547F050D0>,...] 16 # !!注意是 function hello.<locals>.pingfang.<locals>.haha 请联系到特色菜1中print(hello())的函数对象,看出两者的区别和共同性. 17 18 f1, f2, f3 = hello() 19 print(f1()) 20 print(f2()) 21 print(f3())
咳咳,码了这么代码和注释,耗费很长时间啊,希望对您理解闭包(closure)有所帮助.