为什么不是python嵌套函数叫闭包?
我已经看到并在Python中使用嵌套的函数,它们匹配闭包的定义。 那为什么他们叫nested functions
而不是closures
呢?
嵌套函数是不是因为它们不被外部世界使用而关闭?
更新:我正在阅读有关闭包,它让我思考这个关于Python的概念。 我在下面的评论中搜索并找到了某人提到的文章,但是我不能完全理解那篇文章中的解释,所以这就是我提出这个问题的原因。
当一个函数从已经完成执行的封闭作用域访问一个局部变量时,就会发生闭包。
def make_printer(msg): def printer(): print msg return printer printer = make_printer('Foo!') printer()
当make_printer
,将一个新的框架放在堆栈上, printer
函数的编译代码为常量, msg
的值为本地值。 然后创建并返回该函数。 由于函数printer
引用了msg
变量,因此在make_printer
函数返回后它将保持活动状态。
所以,如果你的嵌套函数不
- 访问本地封闭范围的变量,
- 在这个范围之外执行时,
那么他们不是关闭的。
这是一个不是闭包的嵌套函数的例子。
def make_printer(msg): def printer(msg=msg): print msg return printer printer = make_printer("Foo!") printer() #Output: Foo!
在这里,我们将该值绑定到参数的默认值。 当功能printer
被创建时,会发生这种情况,因此在make_printer
返回后, make_printer
维护printer
外部msg
的值。 在这种情况下, msg
只是功能printer
的常规局部变量。
这个问题已经由 aaronasterling 回答了
但是,有人可能会对如何将变量存储在引擎中感兴趣。
在进入片段之前:
闭包是从其封闭环境中继承变量的函数。 将函数回调作为参数传递给另一个将执行I / O的函数时,稍后将调用此回调函数,而且这个函数会 – 几乎神奇 – 记住它所声明的上下文以及所有可用的变量在这方面。
-
如果一个函数不使用自由变量,它不会形成闭包。
-
如果还有另外一个使用自由变量的内部级别 – 所有之前的级别保存词法环境(例如结尾)
-
函数属性
func_closure
在python <3.X或__closure__
在python> 3.X保存自由变量。 -
python中的每个函数都有这个闭包属性,但是如果没有自由变量,它不会保存任何内容。
例如:关闭属性,但没有内容,因为没有自由变量。
>>> def foo(): ... def fii(): ... pass ... return fii ... >>> f = foo() >>> f.func_closure >>> 'func_closure' in dir(f) True >>>
注意: 自由变量必须创建一个关闭。
我将使用与上面相同的代码片段进行解释:
>>> def make_printer(msg): ... def printer(): ... print msg ... return printer ... >>> printer = make_printer('Foo!') >>> printer() #Output: Foo!
所有的Python函数都有一个闭包属性,所以让我们来看看闭包函数的闭包变量。
这是功能printer
的属性func_closure
>>> 'func_closure' in dir(printer) True >>> printer.func_closure (<cell at 0x108154c90: str object at 0x108151de0>,) >>>
closure
属性返回一个元组对象,它包含在封闭范围中定义的变量的细节。
func_closure中的第一个元素可以是None,也可以是包含函数自由变量绑定的单元的元组,它是只读的。
>>> dir(printer.func_closure[0]) ['__class__', '__cmp__', '__delattr__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'cell_contents'] >>>
在上面的输出中,您可以看到cell_contents
,让我们看看它存储的内容:
>>> printer.func_closure[0].cell_contents 'Foo!' >>> type(printer.func_closure[0].cell_contents) <type 'str'> >>>
所以,当我们调用printer()
函数时,它访问存储在cell_contents
的值。 这就是我们如何得到输出为'Foo!'
再次我会解释一下使用上面的代码片段:
>>> def make_printer(msg): ... def printer(): ... pass ... return printer ... >>> printer = make_printer('Foo!') >>> printer.func_closure >>>
在上面的代码片段中,我不打印打印机函数中的msg,所以它不会创建任何自由变量。 由于没有自由变量,关闭内部不会有内容。 这就是我们上面看到的。
现在我将解释另一个不同的片段,以清除所有Free Variable
Closure
:
>>> def outer(x): ... def intermediate(y): ... free = 'free' ... def inner(z): ... return '%s %s %s %s' % (x, y, free, z) ... return inner ... return intermediate ... >>> outer('I')('am')('variable') 'I am free variable' >>> >>> inter = outer('I') >>> inter.func_closure (<cell at 0x10c989130: str object at 0x10c831b98>,) >>> inter.func_closure[0].cell_contents 'I' >>> inn = inter('am')
所以,我们看到一个func_closure
属性是一个闭包单元的元组,我们可以明确地引用它们及其内容 – 一个单元具有属性“cell_contents”
>>> inn.func_closure (<cell at 0x10c9807c0: str object at 0x10c9b0990>, <cell at 0x10c980f68: str object at 0x10c9eaf30>, <cell at 0x10c989130: str object at 0x10c831b98>) >>> for i in inn.func_closure: ... print i.cell_contents ... free am I >>>
在这里,当我们打电话给inn
,它会参考所有的保存自由变量,所以我们得到I am free variable
>>> inn('variable') 'I am free variable' >>>
Python对闭包的支持很弱 。 看看我的意思是使用JavaScript的闭包采取下面的例子:
function initCounter(){ var x = 0; function counter () { x += 1; console.log(x); }; return counter; } count = initCounter(); count(); //Prints 1 count(); //Prints 2 count(); //Prints 3
关闭是非常优雅的,因为它赋予这样写的功能具有“内部存储器”的能力。 从Python 2.7开始,这是不可能的。 如果你尝试
def initCounter(): x = 0; def counter (): x += 1 ##Error, x not defined print x return counter count = initCounter(); count(); ##Error count(); count();
你会得到一个错误,说x没有被定义。 但是,如果其他人已经显示你可以打印它,怎么可能呢? 这是因为它如何管理函数变量作用域。 虽然内部函数可以读取外部函数的变量,但不能写入它们。
这真是太遗憾了 但是只用read-only闭包,你至少可以实现Python为其提供语法糖的函数装饰器模式 。
更新
正如已经指出的,有办法来处理python的范围限制,我会暴露一些。
1.使用global
关键字(通常不推荐)。
2.定义一个简单的可修改的类Object
class Object(object): pass
并在initCounter
创建一个Object scope
来存储变量
def initCounter (): scope = Object() scope.x = 0 def counter(): scope.x += 1 print scope.x return counter
因为scope
实际上只是一个参考,所以在其领域采取的行动并不真正修改scope
本身,所以不会出现错误。
3.另一种方法,如@unutbu指出的,将每个变量定义为一个数组( x = [0]
)并修改它的第一个元素( x[0] += 1
)。 再次没有出现错误,因为x
本身没有被修改。
4.按照@raxacoricofallapatorius的建议,你可以使x
成为counter
一个属性
def initCounter (): def counter(): counter.x += 1 print counter.x counter.x = 0 return counter
我有一种情况,我需要一个单独的,但持久的名称空间。 我使用类。 我不这样做。 隔离但持久的名称是关闭。
>>> class f2: ... def __init__(self): ... self.a = 0 ... def __call__(self, arg): ... self.a += arg ... return(self.a) ... >>> f=f2() >>> f(2) 2 >>> f(2) 4 >>> f(4) 8 >>> f(8) 16 # **OR** >>> f=f2() # **re-initialize** >>> f(f(f(f(2)))) # **nested** 16 # handy in list comprehensions to accumulate values >>> [f(i) for f in [f2()] for i in [2,2,4,8]][-1] 16
def nested1(num1): print "nested1 has",num1 def nested2(num2): print "nested2 has",num2,"and it can reach to",num1 return num1+num2 #num1 referenced for reading here return nested2
得到:
In [17]: my_func=nested1(8) nested1 has 8 In [21]: my_func(5) nested2 has 5 and it can reach to 8 Out[21]: 13
这是关闭的一个例子,以及如何使用它。
Python 2没有关闭 – 它有类似闭包的解决方法。
在已经给出的答案中有很多例子 – 将变量复制到内部函数中,修改内部函数中的对象等。
在Python 3中,支持更加明确 – 简洁:
def closure(): count = 0 def inner(): nonlocal count count += 1 print(count) return inner
用法:
start = closure() start() # prints 1 start() # prints 2 start() # prints 3
nonlocal
关键字将内部函数绑定到明确提到的外部变量,实际上包含它。 因此更明确地说是“关闭”。
我想提供另一个python和JS例子之间的简单比较,如果这有助于使事情更清晰。
JS:
function make () { var cl = 1; function gett () { console.log(cl); } function sett (val) { cl = val; } return [gett, sett] }
并执行:
a = make(); g = a[0]; s = a[1]; s(2); g(); // 2 s(3); g(); // 3
蟒蛇:
def make (): cl = 1 def gett (): print(cl); def sett (val): cl = val return gett, sett
并执行:
g, s = make() g() #1 s(2); g() #1 s(3); g() #1
原因:和其他人一样,在Python中,如果在内部作用域中有一个赋值给同名的变量,那么就会在内部作用域中创建一个新的引用。 除非你用var
关键字明确地声明了一个,否则JS不是这样。