概念:闭包概念:在一个内部函数中,对外部作用域的变量进行引用,(并且一般外部函数的返回值为内部函数),那么内部函数就被认为是闭包。 总结:
- 闭包是一个内嵌函数
- 对外部函数变量引用
- 外部函数返回内嵌函数
闭包是支持一等函数的编程语言(Python、JavaScript等)中实现词法绑定的一种技术。当捕捉闭包的时候,它的自由变量(在函数外部定义但在函数内部使用的变量)会在捕捉时被确定,这样即便脱离了捕捉时的上下文,它也能照常运行。简单的说,可以将闭包理解为能够读取其他函数内部变量的函数。正在情况下,函数的局部变量在函数调用结束之后就结束了生命周期,但是闭包使得局部变量的生命周期得到了延展。使用闭包的时候需要注意,闭包会使得函数中创建的对象不会被垃圾回收,可能会导致很大的内存开销,所以闭包一定不能滥用。
def start_at(x):
def increment_by(y):
return x + y
return increment_by
if __name__ == '__main__':
a = start_at(1)
print(a) # <function start_at.<locals>.increment_by at 0x0000017C576392F0>
print(a(1)) # 2
- 在函数start_at中定义了一个increment_by函数,increment_by访问了外部函数start_at的变量,并且函数返回值为increment_by函数(注意python是可以返回一个函数的,这也是python的特性之一)
- 上面a其实函数increment_by,因为函数start_at返回值是函数increment_by
- 调用a,
a(1)
就是调用函数increment_by
下面有一段代码,说出他的结果:
def multiply():
return [lambda x: i * x for i in range(4)]
print([m(100) for m in multiply()])
lambda表达式
- lambda表达式只允许包含一个表达式,不能包含复杂语句,该表达式的运算结果就是函数的返回值。
- 语法:
lambda x, y: x*y;函数输入是x和y,输出是它们的积x*y
列表生成式
语法格式:
[exp for iter_var in iterable]
工作流程:
- 迭代iterable中的每个元素;
- 每次迭代都先把结果赋值给iter_var,然后通过exp得到一个新的计算值;
- 最后把所有通过exp得到的计算值以一个新列表的形式返回。
上面代码可以改成:
(理解难点)
def multiply():
func_list = []
for i in range(4):
def lam(x):
return x*i
func_list.append(lam)
return func_list
print([m(100) for m in multiply()])
- func_list是一个包含四个函数的列表:
[<function multiply.<locals>.lam at 0x000001B6B2999378>, <function multiply.<locals>.lam at 0x000001B6B2999400>, <function multiply.<locals>.lam at 0x000001B6B2999488>, <function multiply.<locals>.lam at 0x000001B6B2999510>]
- multiply函数返回的是个包含四个函数的列表func_list
m(100) for m in multiply()
:相当于:for m in multiply():
m(100)
遍历multiply函数返回的四个函数的列表func_list —> 即调用四个函数lam,每个函数传入值100,即运行m(100) —> lam(100)
- (理解难点)当执行lam(100)时,发现变量 i 并不是foo()中的变量,于是就到外部函数func中寻找变量 i ,但此时外部的 for 已经循环完毕,最后的 i =3 。所以,每次执行m(100),i 的值都是 3 ,因此,最终结果会是 [300,300,300,300] (容易错误认为是:[0, 100, 200, 300])。
现在再看代码:
def multiply():
func_list = []
for i in range(4):
def lam(x):
return x*i
func_list.append(lam)
return func_list
print([m(100) for m in multiply()])
上面代码运行步骤:
- 代码从第9行开始运行,解释器碰到了一个列表解析,循环取multiply()函数中的值,而multiply()函数返回的是一个列表对象,这个列表中有4个元素,每个元素都是一个匿名函数
- 补充知识点:在python中,相对而言的局部变量绑定的是值,非局部变量绑定的是空间,而不是值得本身。
- 在上面得例子中,for循环得 i,相对于lam函数而言,i 是全局变量,所以绑定得是空间,最终导lam函数运行得时候,读取得是最后保存i的值(i = 3)
- 如果想要结果是
[0, 100, 200, 300]
,解决办法是通过函数的默认赋值,达到保存临时变量的作用 ```python def multiply(): func_list = [] for i in range(4): def lam(x,n=i):
func_list.append(lam) return func_listreturn x*n
print([m(100) for m in multiply()])
这时候因为 n 是局部变量,所以绑定的是 n 的值,而n的值默认等于外部i的值
原代码结果要想是[0, 100, 200, 300]:<br />方式一:
```python
def multiply():
return [lambda x,i=i: i * x for i in range(4)]
print([m(100) for m in multiply()])
方式二:可以使用生成器
def multiply():
return (lambda x: i * x for i in range(4))
print([m(100) for m in multiply()])
或者
def multiply():
for i in range(4):
yield lambda x: x * i
print([m(100) for m in multiply()])