(1)变量的域
要了解闭包需要先了解变量的域, 也就是变量在哪一段 "上下文" 是有效的(类似局部变量和全局变量的区别), 举一个很简单的例子.(例子不重要, 就是涉及闭包就要时刻关注这个域)
- def test():
- msg2 = 'test 中的'
- print('====',msg1) # ==== 非 test 中的
- msg1 = '非 test 中的'
- test()
- print(msg1) # 非 test 中的
- print(msg2) # 报错
(2)什么是闭包
维基百科定义: 闭包 (Closure) 或闭包函数(function Closure), 是引用了自由变量的函数. 这个被引用的自由变量将和这个函数一同存在, 即使已经离开了创造它的环境也不例外. 所以有另一种说法认为闭包是由函数和与其相关的引用环境组合而成的实体.
定义可以反复体会, 先看一个比较有意思的例子:
- def test():
- msg = '我是 test 中的'
- def test2():
- print(msg)
- return test2
- g = test()
- g() # 我是 test 中的
这段代码执行到第七行的时候, 输出了 msg 的内容.
我们在对比 [(1) 变量的域] 中的代码例子, 那个例子中, 同样是第七行的时候是报错, 因为很好理解, 上一个例子 msg2 已经脱离了 test()函数, 也就是局部变量只能在内部使用, 不能够全局使用.
然后回到这个例子, 这就是闭包存在的意义, 我们可以在外部访问局部变量. 闭包就是: 函数 + 上下文. 注意到我这个例子第五行返回的是 test2, 是一个函数对象 (in Python everything is an object ). 这里的 g 就是闭包. 而我所说的上下文: 就是各种变量环境. 所以第七行的 return, 返回的 function 不是普通 function, 是带着上下文环境一起的(也就是 test2() 函数中带有 msg, 而 msg 其实实在 test() 中定义的, 但是也会被 test2()带在身边). 不单单只返回第三第四行两行简单的内容.
(3)闭包疑点
我们再看几个 stockoverflow 和 官网上的几个关于闭包的例子与疑点:
1. 例子一
- adders=[0,1,2,3]
- for i in [0,1,2,3]:
- adders[i]=lambda a: i+a
- print(adders[1](3)) # 6
这个里之中 adders 列表存储了匿名函数, adders[1](3) 就是访问 adders[1] 中的匿名函数, 参数是 3, 也就是 lambda a:i+a(3 传递给 a,i 是 for...in 循环给的, 计算结果是 i + a).
奇怪的是结果 adders[1](3) = 6. 我们可能会想应该是 4 阿, 1 + 3 = 4.
我的理解是这样的: 因为我们需要注意这里的 i 到底是属于谁的, i 是在 for...in 中定义的, 一个循环至始至终只有一个 i , 也就是 i 的引用是不变的, 变得是 i 得值, 所以 lambda 中的 i ,adders[0],adders[1].... 中的 i 都是指向同一个 i , 而最后 i 是三. adders[0,1,2,3] 中的 lambda 匿名函数的参数 i 全都是同一个, 这个 i 因为循环最终值是 3. 所以 3+3=6.(也就是 i 是什么时候定义的? 这个问题考虑好, 是在调用 lambda 之前, 也就是 for 循环开始的时候定义好的)
改进方案:
- adders=[0,1,2,3]
- for i in [0,1,2,3]:
- adders[i]=lambda a, b = i: b+a
- print(adders[0](3)) # 3
- print(adders[1](3)) # 4
- print(adders[2](3)) # 5
- print(adders[3](3)) # 6
那我们就在 lambda 之中定义一个 b, 这个 b 是记录 i, 但是 adders[ ....] 数组中的 b 是各不相同的引用哦.
2. 例子二
- squares = []
- for x in range(5):
- squares.append(lambda: x**2)
- print(squares[2]()) # 16
- print(squares[3]()) # 16
情况一模一样. 最后全都算 4 *4 = 16. 改进如下:
- squares = []
- for x in range(5):
- squares.append(lambda b = x: b**2)
- print(squares[2]()) # 4
- print(squares[3]()) # 9
来源: https://www.cnblogs.com/KongHuZi/p/10889437.html