[关闭]
@chengweihuang 2019-04-19T01:55:17.000000Z 字数 1182 阅读 456

闭包

闭包


什么是闭包

  1. def fun_out(a):
  2. def fun_in(b):
  3. return a + b
  4. return fun_in
  5. fun1 = fun_out(1)

其中fun1函数是一个闭包,它携带着fun_out中定义的a变量,值为1。运行程序的效果是这样的.

  1. >>> fun1 = fun_out(1)
  2. >>> fun1(3)
  3. 4
  4. >>> fun1(6)
  5. 7
  6. >>> fun5 = fun_out(5)
  7. >>> fun5(3)
  8. 8
  9. >>> fun5(6)
  10. 11

fun1和fun5两个函数的定义相同,只是携带的自由变量不同,便成为了两个函数。由此闭包可以作为函数工厂,生产出功能类似,但是会有细微差别的函数。(与类相似)

为了能够更深入地理解闭包,我们需要对变量作用域进行说明,以下面代码为例 这里fun_in函数是一个闭包吗?

  1. d = 3
  2. def fun_out():
  3. a = 1
  4. def fun_in(c):
  5. b = 2
  6. return b
  7. return fun_in

在fun_in函数中调用一个变量,搜索路径如下

先在fun_in函数定义的局部空间中开始搜索,这部分变量包括上面的b c
如果在局部空间中找不到,则在全局空间中搜索,即d所在位置
如果还是找不到会去找python内置变量
如果还是找不到则抛出异常
这里有一个问题,为什么没有提到变量a所在空间?

因为按常理来说,函数fun_out运行结束后,这个函数中定义的变量是不可以被其他函数直接引用的,也就是说这里的变量只能函数fun_out它自己用。
但是因为闭包的特性,如果在fun_in函数中引用了a变量(上面代码中没有),则fun_out运行return fun_in时,会将a变量绑定到fun_in函数上返回,因此此时(是闭包时)fun_in函数是可以搜索到变量a的
这就导致了一个现象,即如果fun_in函数是一个闭包,则在搜索变量时,实际上相当于是按照这样的顺序:局部->非局部非全局(a位置)->全局->内置->抛出异常

  1. def fun_out(a):
  2. def fun_in(b):
  3. return a + b
  4. return fun_in
  5. fun1 = fun_out(99)
  6. print(fun1.__closure__) # 自由变量
  7. #<cell at 0x00000015BBADC5B8: int object at 0x00000000725C7850>
  8. #print(fun1.__closure__[0].cell_contents)
  9. # 如果没有引用 a 则打印None

闭包等价——类

  1. class Add:
  2. def __init__(self, b):
  3. self.b = b
  4. def __call__(self, a):
  5. return a + self.b
  6. add1 = Add(1)
  7. print(add1(3))
  8. add5 = Add(5)
  9. print(add5(3))
  10. #__call__是一个很神奇的特性,只要某个类型中有__call__方法,,我们可以把这个类型的对象当作函数来使用。
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注