问题:为什么python嵌套函数不称为闭包?
我已经在Python中看到并使用了嵌套函数,它们与闭包的定义匹配。那么为什么叫他们nested functions
而不是closures
?
嵌套函数不是因为没有被外部世界使用而不是闭包吗?
更新:我正在阅读有关闭包的知识,这让我开始思考关于Python的这个概念。我搜索并找到了某人在下面的评论中提到的文章,但是我无法完全理解该文章中的解释,所以这就是为什么我问这个问题。
回答 0
当函数可以从完成其执行的封闭范围访问局部变量时,就会发生关闭。
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
创建函数时会发生这种情况,因此返回 后无需保留对msg
external 值的引用。在这种情况下只是函数的普通局部变量。printer
make_printer
msg
printer
回答 1
这个问题已经由 aaronasterling 回答
但是,可能有人会对变量如何在后台存储感兴趣。
在摘录之前:
闭包是从其封闭环境中继承变量的函数。当您将函数回调作为参数传递给将要执行I / O的另一个函数时,此回调函数将在以后被调用,并且该函数-几乎是神奇的-记住声明它的上下文以及所有可用变量在这种情况下。
如果函数不使用自由变量,则不会形成闭包。
如果还有另一个使用自由变量的内部级别- 所有以前的级别都保存词法环境(末尾的示例)
功能属性
func_closure
在蟒<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函数都有一个Closure属性,因此让我们检查与Closure函数关联的封闭变量。
这是func_closure
函数的属性printer
>>> '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
,它将引用所有save free变量,因此我们得到I am free variable
>>> inn('variable')
'I am free variable'
>>>
回答 2
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。但是,如果别人显示可以打印,那怎么办?这是因为Python如何管理函数变量范围。虽然内部函数可以读取外部函数的变量,但不能写入它们。
真是可惜。但是,仅使用只读闭包,就可以至少实现Python提供语法糖的函数装饰器模式。
更新资料
正如其指出的那样,有一些方法可以应对python的范围限制,我将介绍一些方法。
1.使用global
关键字(通常不建议使用)。
2.在Python 3.x中,使用nonlocal
关键字(由@unutbu和@leewz建议)
3.定义一个简单的可修改类Object
class Object(object):
pass
并创建一个 Object scope
内部initCounter
存储变量
def initCounter ():
scope = Object()
scope.x = 0
def counter():
scope.x += 1
print scope.x
return counter
由于scope
实际上仅是参考,因此对其字段执行的操作不会真正对其scope
自身进行修改,因此不会出现错误。
4. @unutbu指出,另一种方法是将每个变量定义为数组(x = [0]
)并修改其第一个元素(x[0] += 1
)。同样,不会发生错误,因为x
它本身没有被修改。
5.根据@raxacoricofallapatorius的建议,您可以x
设置counter
def initCounter ():
def counter():
counter.x += 1
print counter.x
counter.x = 0
return counter
回答 3
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
关键词结合的内函数来明确提到的外变量,实际上包围它。因此,更明确地说是“关闭”。
回答 4
我遇到的情况是我需要一个单独的但持久的命名空间。我上过课。我不是这样。隔离但持久的名称是闭包。
>>> 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
回答 5
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
这是什么是闭包以及如何使用闭包的示例。
回答 6
我想在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
Python:
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中,如果内部作用域中有一个同名变量的赋值,则会在内部作用域中创建一个新引用。JS并非如此,除非您使用var
关键字明确声明一个。