您的位置:首页 > 编程语言 > Python开发

【Python】什么是闭包

2016-04-30 18:32 585 查看
文章转载自:点这里

在 Python 中很多教材都没有提及什么是闭包,但在定义一个 Decorator 时,就已经用到闭包了。如果不理解什么是闭包,则不可能清晰掌握Decorator 装饰器。

要形成
闭包
首先得有一个嵌套的函数,即函数中定义了另一个函数,闭包则是一个集合,它包括了外部函数的局部变量,这些局部变量在外部函数返回后也继续存在,并能被内部函数引用。

1. 先简单理解一下什么是闭包

举个例子:

这是个经常使用到的例子,定义一个函数
generate_power_func
,它返回另一个函数,现在闭包形成的条件已经达到。

def generate_power_func(n):
print "id(n): %X" % id(n)
def nth_power(x):
return x**n
print "id(nth_power): %X" % id(nth_power)
return nth_power

对于内部函数
nth_power
,它能引用到
外部函数的局部变量 n
,而且即使 generate_power_func 已经返回。把这种现象就称为
闭包
。具体使用一下。

>>> raised_to_4 = generate_power_func(4)
id(n): 246F770
id(nth_power): 2C090C8
>>> repr(raised_to_4)
' '

从结果可以看出,当
generate_power_func(4)
执行后, 创建和返回了
nth_power
这个函数对象,内存地址是
0x2C090C8
,并且发现
raised_to_4
和它的内存地址相同,即
raised_to_4
只是这个函数对象的一个引用。先在全局命名空间中删除
generate_power_func
,再试试会出现什么结果。

>>> del generate_power_func
>>> raised_to_4(2)
16

啊哈,居然没出现错误,
nth_power
是怎么知道 n 的值是 4,而且现在 在 generate_power_func 甚至都不在这个命名空间了。对,这就是闭包的作用,外部函数的局部变量可以被内部函数引用,即使外部函数已经返回了。

_closure_
属性和 cell 对象

现在知道闭包是怎么一回事了,那就到看看闭包到底是怎么回事的时候了。

Python
中函数也是对象,所以函数也有很多属性,和闭包相关的就是
__closure__
属性。
__closure__
属性定义的是一个包含
cell 对象的元组
,其中元组中的每一个
cell
对象用来保存作用域中变量的值

>>> raised_to_4.__closure__
(,)
>>> type(raised_to_4.__closure__[0])>>> raised_to_4.__closure__[0].cell_contents
4

就如刚才所说,在
raised_to_4
__closure__
属性中有外部函数变量 n 的引用,通过内存地址可以发现,引用的都是同一个 n。如果没用形成闭包,则 _
_closure__
属性为 None。对于 Python 具体是如何实现闭包的,可以查看 Python闭包详解,它通过分析 Python 字节码来讲述闭包的实现。

2. 进一步加深闭包理解

一个函数闭包是一个函数和一个引用集合的组合,这个引用集合指向这个函数被定义的作用域的变量。后者通常指向一个引用环境(
referencing environment
),这使得函数能够在它被定义的区域之外执行。在
Python
中,这个引用环境被存储在一个
cell
tuple
中。你能够通过
func_closure
或Python 3中的
__closure__
属性访问它。要铭记的一点是引用及是引用,而不是对象的深度拷贝。当然了,对于不可变对象而言,这并不是问题,然而对可变对象(list)这点就必须注意,随后会有一个例子说明。请注意函数在定义的地方也有
__globals__
字段来存储全局引用环境。

来看一个简单的例子:

>>> def return_func_that_prints_s(s):
...     def f():
...             print s
...     return f
...
>>> g = return_func_that_prints_s("Hello")
>>> h = return_func_that_prints_s("World")
>>> g()
Hello
>>> h()
World
>>> g is h
False
>>> h.__closure__
(,)
>>> print [str(c.cell_contents) for c in g.__closure__]
['Hello']
>>> print [str(c.cell_contents) for c in h.__closure__]
['World']

一个稍复杂的例子。确保明白为什么会这么执行。

>>> def return_func_that_prints_list(z):
...     def f():
...             print z
...     return f
...
>>> z = [1, 2]
>>> g = return_func_that_prints_list(z)
>>> g()
[1, 2]
>>> z.append(3)
>>> g()
[1, 2, 3]
>>> z = [1]
>>> g()
[1, 2, 3]

【译者】:z.append(3)时,g()内部的引用和z仍然指向一个变量,而z=1之后,两者就不再指向一个变量了。

最后,来看看代码中使用到的dump_closure方法的定义。

def dump_closure(f):
if hasattr(f, "__closure__") and f.__closure__ is not None:
print "- Dumping function closure for %s:" % f.__name__
for i, c in enumerate(f.__closure__):
print "-- cell %d  = %s" % (i, c.cell_contents)
else:
print " - %s has no closure!" % f.__name__

参考

https://serholiu.com/python-closures

http://blog.jobbole.com/66895/

http://www.cnblogs.com/yuyan/archive/2012/04/21/2461673.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: