【Python】什么是闭包
2016-04-30 18:32
645 查看
文章转载自:点这里
在 Python 中很多教材都没有提及什么是闭包,但在定义一个 Decorator 时,就已经用到闭包了。如果不理解什么是闭包,则不可能清晰掌握Decorator 装饰器。
要形成
这是个经常使用到的例子,定义一个函数
对于内部函数
从结果可以看出,当
啊哈,居然没出现错误,
现在知道闭包是怎么一回事了,那就到看看闭包到底是怎么回事的时候了。
就如刚才所说,在
来看一个简单的例子:
一个稍复杂的例子。确保明白为什么会这么执行。
【译者】:z.append(3)时,g()内部的引用和z仍然指向一个变量,而z=1之后,两者就不再指向一个变量了。
最后,来看看代码中使用到的dump_closure方法的定义。
http://blog.jobbole.com/66895/
http://www.cnblogs.com/yuyan/archive/2012/04/21/2461673.html
在 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-closureshttp://blog.jobbole.com/66895/
http://www.cnblogs.com/yuyan/archive/2012/04/21/2461673.html
相关文章推荐
- 【Python】高阶函数介绍
- Python break和continue
- python使用zlib实现压缩与解压字符串
- 安装mysql-python:EnvironmentError: mysql_config not found
- python基础(一)
- Python核心编程 第七章练习
- python day2:python 初识(二)
- python学习之旅之python简单命令
- python range()和xrange()
- windows系统下Python3.5.1安装教程
- Python 深浅拷贝
- 用python实现socket通信
- Python collections类
- Python2.7.11安装matplotlib,numpy,scikit-learn等其他包,w7x86x64下无问题
- python检测字符串是否只由数字组成
- python中list的赋值处理
- theano, sklearn--- win10 64 bit 配置anaconda(Python2.7)
- [python]Python Fabric - No hosts found. Please specify (single) host string for connection
- python条件语句嵌套
- Python并行处理——多线程