python自学Python

Python中的闭包是什么?

2018-04-10  本文已影响9人  苦逼李

目录


2018.4.15
更新了对于函数作用域的理解内容

1.作用域

作用域限定了一个变量在程序中的有效范围,对于一个函数,其内部定义的变量的作用域就是这个函数体内部,一旦函数结束被调用,此变量将被析构。简言之,函数中定义的变量称作局部变量,它只能在函数内部被引用。

下面的例子说明了局部变量不能在函数外访问:

In [9]: def f1():
   ...:     a = 1
   ...:

In [10]: print(a)
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-10-bca0e2660b9f> in <module>()
----> 1 print(a)

NameError: name 'a' is not defined

而在所有函数之外定义的变量可以被函数访问:

In [12]: b = 1

In [13]: def f2():
    ...:     print(b)
    ...:

In [14]: f2()
1

另外,由于Python支持函数的嵌套,按照函数的作用域原则,内函数可以访问外函数定义的变量:

In [37]: def f1():
    ...:     x = 1
    ...:     def f2():
    ...:         print(x)
    ...:     f2()
    ...:

In [38]: f1()
1

2.闭包的定义

我们将上一个代码稍作修改:

In [50]: def f1():
    ...:     x = 1
    ...:     def f2():
    ...:         print(x)
    ...:     return f2
    ...:

In [51]: m = f1()

In [52]: n = f1()

In [53]: m == n
Out[53]: False
    
In [54]: m()
Out [54]: 1

通过上述的例子,我们再引入Wiki百科的对于闭包的定义:

在计算机科学中,闭包(英语:Closure),又称词法闭包(Lexical Closure)或函数闭包(function closures),是引用了自由变量的函数。这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外。另一种说法认为闭包是由函数和与其相关的引用环境组合而成的实体。

如果要在内嵌函数中修改引用的外部变量,需要在内嵌函数中对要修改的变量使用nonlocal关键字进行声明:

In [58]: def creatCounter():    # 实现一个加法器 每次调用内嵌函数就加一
    ...:     i = 0
    ...:     def count():
    ...:         nonlocal i     # nonlocal关键字标志着内嵌函数将修改外部变量 如果没有这行代码将会出错
    ...:         i += 1
    ...:         return i
    ...:     return count
    ...:

In [59]: f = creatCounter()

In [60]: f()
Out[60]: 1

In [61]: f()
Out[61]: 2

In [62]: f()
Out[62]: 3

3.闭包的作用

闭包避免使用了全局变量,闭包允许将某些数据和函数关联起来,这一点很像类。在面向对象过程中,我们将定义了一些属性,并将它们与一些方法关联起来。如果要定义只用一个方法的类,可以采用闭包的方法实现。另外,闭包在装饰器中也很重要。

判断一个函数是不是闭包,可以查看它的closure属性,如果是闭包,查看该属性将会返回一个cell对象组成的元组,分别对每个cell对象查看其cell_contents属性,返回的内容就是闭包引用的自由变量的值。下面通过一个例子展示:

In [41]: def add(x,y):
    ...:     def f(z):
    ...:         return x+y+z
    ...:     return f
    ...:

In [42]: d = add(5,5)

In [43]: d(9)
Out[43]: 19

In [44]: d(1)
Out[44]: 11

In [45]: d.__closure__      # 闭包的__closure__属性
Out[45]:
(<cell at 0x000001F9A295CCD8: int object at 0x000000006F666140>,
 <cell at 0x000001F9A295C9A8: int object at 0x000000006F666140>)

In [46]: for i in d.__closure__:
    ...:     print(i.cell_contents)   # 查看每个cell对象的内容 —— cell_contents属性
    ...:
5
5

4.总结

结语:这篇文章更多的只是作为学习笔记,其中参考了网上一些前辈的文章或是其他资料,所以有些可能会大量引用,如有冒犯,请私信我,还望谅解。

上一篇下一篇

猜你喜欢

热点阅读