Python闭包
1. 什么是闭包, 闭包必须满足以下3个条:
- 必须是一个嵌套函数
- 闭包必须返回嵌套的函数
- 嵌套函数必须引用一个外部的非全局的变量
2. 闭包的优点
- 避免使用全局变量
- 可以提供部分数据的隐藏
- 可以提供更优雅的面向对象实现
def Add(a):
def add(b):
return a + b
return add
ad(2)(2) # 计算2+2的值, 用类实现的话, 相对麻烦
# 闭包使用nonlocal
def tester(start):
state = start
def nested(label):
nonlocal state
print(label, state)
state += 1
return nested # 结合nonlocal使用的话还能实现意想不到的效果, 初始情况下给start传入不同的值, 会保存到不同的对象
def test(start):
global state
state = start
def nest(label):
global state
print(label, state)
state += 1
return nest # global的话初始情况下给start无论传入什么值, 操作的始终都是一个对象, 达不到类实现的效果
# 类实现nonlocal的效果
class MyClass:
def __init__(self, start):
self.start = start
def nest(self, label):
print(label, self.start)
self.start += 1
3. 闭包的延迟绑定
def multipliers():
return [lambda x : i*x for i in range(4)]
print([m(2) for m in multipliers()])
# 打印结果是[6, 6, 6, 6], 而不是[0, 2, 4, 6]
# 原因: i不仅仅控制着循环次数, 而且还在为i*x的i提供引用的值, 当Python解释器运行的时候, 调用multipliers()的时候, 只会先定义lambda匿名函数, 但是却会直接运行后面的for循环, 当lambda被调用的时候, i已经指向了4, 这就是延迟绑定的特性(Python中非局部变量绑定的是内存地址, 局部变量绑定的是值)
4. 延迟绑定的解决办法
方法一:
def multipliers():
return [lambda x, i=i: i*x for i in range(4)]
print([m(2) for m in multipliers()])
# 打印结果是[0, 2, 4, 6]
# Python解释器在定义函数时, 遇到关键字参数, 就必须初始化参数, 此时后面的for循环每循环一次, 前面的关键字参数i就需要找一次引用对象, for循环每循环一次结果i都会被i这个关键字参数指向, 这个指向的是值, 不是空间
方法二: Python生成器
def f1():
for i in range(4): yield lambda x: x * i
print(list((m(2) for m in f1())))
# list()中是()这个是生成器的推导式(这种方法可以简单的调用上面的函数内部的生成器)