概念:闭包概念:在一个内部函数中,对外部作用域的变量进行引用,(并且一般外部函数的返回值为内部函数),那么内部函数就被认为是闭包。 总结:

  1. 闭包是一个内嵌函数
  2. 对外部函数变量引用
  3. 外部函数返回内嵌函数

闭包是支持一等函数的编程语言(Python、JavaScript等)中实现词法绑定的一种技术。当捕捉闭包的时候,它的自由变量(在函数外部定义但在函数内部使用的变量)会在捕捉时被确定,这样即便脱离了捕捉时的上下文,它也能照常运行。简单的说,可以将闭包理解为能够读取其他函数内部变量的函数。正在情况下,函数的局部变量在函数调用结束之后就结束了生命周期,但是闭包使得局部变量的生命周期得到了延展。使用闭包的时候需要注意,闭包会使得函数中创建的对象不会被垃圾回收,可能会导致很大的内存开销,所以闭包一定不能滥用

  1. def start_at(x):
  2. def increment_by(y):
  3. return x + y
  4. return increment_by
  5. if __name__ == '__main__':
  6. a = start_at(1)
  7. print(a) # <function start_at.<locals>.increment_by at 0x0000017C576392F0>
  8. print(a(1)) # 2
  1. 在函数start_at中定义了一个increment_by函数,increment_by访问了外部函数start_at的变量,并且函数返回值为increment_by函数(注意python是可以返回一个函数的,这也是python的特性之一)
  2. 上面a其实函数increment_by,因为函数start_at返回值是函数increment_by
  3. 调用a,a(1)就是调用函数increment_by

下面有一段代码,说出他的结果:

  1. def multiply():
  2. return [lambda x: i * x for i in range(4)]
  3. print([m(100) for m in multiply()])

要清楚上面代码运行结果,需要了解以下知识:

lambda表达式

  1. lambda表达式只允许包含一个表达式,不能包含复杂语句,该表达式的运算结果就是函数的返回值
  2. 语法:lambda x, y: x*y;函数输入是x和y,输出是它们的积x*y

列表生成式

语法格式:

  1. [exp for iter_var in iterable]

工作流程:

  • 迭代iterable中的每个元素;
  • 每次迭代都先把结果赋值给iter_var,然后通过exp得到一个新的计算值;
  • 最后把所有通过exp得到的计算值以一个新列表的形式返回。

上面代码可以改成:
(理解难点)

  1. def multiply():
  2. func_list = []
  3. for i in range(4):
  4. def lam(x):
  5. return x*i
  6. func_list.append(lam)
  7. return func_list
  8. 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():相当于:

    1. for m in multiply():
    2. 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])。

现在再看代码:

  1. def multiply():
  2. func_list = []
  3. for i in range(4):
  4. def lam(x):
  5. return x*i
  6. func_list.append(lam)
  7. return func_list
  8. print([m(100) for m in multiply()])

上面代码运行步骤:

  1. 代码从第9行开始运行,解释器碰到了一个列表解析,循环取multiply()函数中的值,而multiply()函数返回的是一个列表对象,这个列表中有4个元素,每个元素都是一个匿名函数
  2. 补充知识点:在python中,相对而言的局部变量绑定的是值,非局部变量绑定的是空间,而不是值得本身。
    1. 在上面得例子中,for循环得 i,相对于lam函数而言,i 是全局变量,所以绑定得是空间,最终导lam函数运行得时候,读取得是最后保存i的值(i = 3)
    2. 如果想要结果是[0, 100, 200, 300],解决办法是通过函数的默认赋值,达到保存临时变量的作用 ```python def multiply(): func_list = [] for i in range(4): def lam(x,n=i):
      1. return x*n
      func_list.append(lam) return func_list

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()])