接下来。装逼开始....
闭包
示例1:计算平均值
def average():
number = []
def handle_avg(values):
number.append(values)
total = sum(number)
return total / len(number)
return handle_avg
avg = average()
print(avg(10))
外函数运行并打印
avg = average()
print(avg)
<function average.<locals>.handle_avg at 0x7fc654f9cc10>
上述返回结果中可以看出,外函数结束时,发现将来会有人使用它的内部变量number,<locals>返回当前所在最小命名空间的局部变量的一个拷贝,拷贝局部变量number给handle_avg使用
内函数运行并打印
avg = make_average()
print(avg(10))
print(avg(11))
10.0
10.5
Process finished with exit code 0
如果avg后面不跟括号就是显示函数名,如果avg加上括号就是要调用这个函数...
注意:
number_data是make_average函数的局部变量,当avg(10)调用时,make_average函数运行结束并已经返回了(本地作用域number_data变量也跟着释放了)
在average函数中,number_data是未绑定本地作用域的自由变量(未在该函数内部定义)
接下来查看average函数返回的对象,利用python中的__code__属性来获取保存的局部变量和自由变量的名称
-
__code__:返回已编译的函数对象
-
__code__.co_varnames:将函数局部变量以元组的形式返回。
-
__code__.co_freevars:返回内部函数中引用外部函数参数。
示例1:通过co_varnames属性获取局部变量
avg = make_average()
print(avg(10))
print(avg(11))
print(avg.__code__.co_varnames)
10.0
10.5
('values', 'total')
Process finished with exit code 0
从上述代码中可以看到,内函数的局部变量有values、total...
示例2:通过co_freevars属性获取自由变量
avg = make_average()
print(avg(10))
print(avg(11))
print(avg.__code__.co_freevars)
10.0
10.5
('number_data',)
Process finished with exit code 0
从上述返回结果看,可以明显看出已经编译的函数对象中已经存着自由变量number_data了... 这样就解释了为什么局部函数能能使用外函数的局部变量了。
number_data的真正值查看,得要使用__closure__属性,返回的是cell对像(可调用对象)
-
__closure__返回一个元祖,元祖中是cell对象
-
cell_contents 可以理解为获取对象的值
示例3:通过__closure__属性获取值
avg = make_average()
print(avg(10))
print(avg(11))
print(avg.__closure__)
print(avg.__closure__[0].cell_contents)
10.0
10.5
(<cell at 0x7f9a5ebddfd0: list object at 0x7f9a5e856140>,)
[10, 11]
Process finished with exit code 0
上述代码可以看出number_data的值都绑定在avg函数的__closure__属性中,而cell_contents属性是保存真正的值
综上,闭包是一种函数,它会保留定义函数时存在的自由变量的绑定,这样调用函数时虽然定义的作用域不可用了,但仍能使用那些绑定
注意,只有嵌套在其他函数中的函数才可能需要处理不在全局作用域中的外部变量...
--->流畅的python
nonlocal声明
示例1:计算平均值,这段代码是会报错的,主要是演示作用
def make_average():
count = 0
total = 0
def average(values):
count += 1
total += values
return total / count
return average
avg = make_average()
print(avg(10))
Traceback (most recent call last):
File "/Users/lifeng/python-projects/Test/pythonScripts/python_reduce.py", line 46, in <module>
print(avg(10))
File "/Users/lifeng/python-projects/Test/pythonScripts/python_reduce.py", line 39, in average
count += 1
UnboundLocalError: local variable 'count' referenced before assignment
Process finished with exit code 1
为什么会报错:
在上上面代码中出现的number_data没有报错的原因是因为,在内函数中并没有定义,只是调用了执行了列表插入动作,也是利用了列表是可变的对象的事实
但是对数字、字符串、元祖这类不可变类型来说,是只能读取不能更新的,如果尝试想更新,例:count+=1,其实就是隐式的创建了一个局部变量,这样count就变成局部变量,自然就不会保存在闭包中了,total同count一样。
对于这类问题,python也给了解决方法,就是引入nonlocal声明,它的作用就是把变量标记为自由变量,即使在函数中被赋予了新值,也会变成自由变量;变量被赋了新值,闭包中保存的绑定也会跟着更新...
示例2:增加nonlocal声明
def make_average():
count = 0
total = 0
def average(values):
nonlocal count, total
count += 1
total += values
return total / count
return average
avg = make_average()
print(avg(10))
print(avg(11))
10.0
10.5
Process finished with exit code 0
至此,闭包的了解告了一段落,后面就可以利用闭包实现装饰器了
以上总结或许能帮助到你,或许帮助不到你,但还是希望能帮助到你,如有疑问、歧义,评论区留言会及时修正发布,谢谢!
未完,待续…
一直都在努力,希望您也是
微信搜索公众号:就用python