nested functions
而不是closures
?嵌套函数不是闭包,因为它们没有被外部世界使用吗?
更新:我正在阅读闭包及其相关内容让我想到了关于Python的这个概念。我搜索并找到了某人在下面的评论中提到的文章,但是我无法完全理解该文章中的解释,所以这就是为什么我问这个问题。
#1 楼
当函数可以从已完成执行的封闭范围访问局部变量时,就会发生关闭。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
之后无需维护对printer
外部的make_printer
值的引用。在这种情况下,msg
只是函数printer
的普通局部变量。 评论
您的回答比我的要好得多,您的观点很好,但是如果我们要遵循最严格的函数式编程定义,那么您的示例甚至是函数?已经有一段时间了,我不记得严格的函数式编程是否允许不返回值的函数。如果您认为返回值是None,那是没有意义的,但这是另一个主题。
–mikerobi
10-10-26在4:08
@mikerobi,我不确定我们是否需要考虑函数式编程,因为python并不是真正的函数式语言,尽管它当然可以使用。但是,不,内部函数不是那种意义上的函数,因为它们的全部目的是产生副作用。创建一个可以很好地说明要点的函数很容易,
–aaronasterling
10-10-26在4:26
@mikerobi:一小段代码是否为闭包取决于其是否在其环境中关闭,而不是您所谓的闭包。它可以是例程,函数,过程,方法,块,子例程等。在Ruby中,方法不能是闭包,只有块可以。在Java中,方法不能是闭包,但类可以。但这并没有使它们变得封闭。 (尽管它们只对某些变量封闭,而不能对其进行修改,这使它们几乎无用。)您可能会说方法只是对自身封闭的过程。 (在JavaScript / Python中几乎是正确的。)
–Jörg W Mittag
2010-10-26 12:39
@JörgWMittag请定义“关闭”。
– Evgeni Sergeev
16年5月2日在4:05
@EvgeniSergeev“关闭”,即是指“来自封闭范围的局部变量[例如,i]”。指的是,即使/当该范围“已完成其执行”,即程序的执行已经进行到代码的其他部分时,也可以检查(或更改)i的值。定义i的块不再存在,但引用i的函数仍然可以这样做。通常将其描述为“结束变量i”。为了不处理特定变量,可以将其实现为封闭定义该变量的整个环境框架。
–尼斯
16年8月16日在9:13
#2 楼
aaronasterling已经解决了这个问题。但是,可能有人对变量如何存储在底层感兴趣。
在摘录之前:
闭包是从封闭环境中继承变量的函数。当您将函数回调作为参数传递给将要执行I / O的另一个函数时,此回调函数将在以后被调用,并且该函数-几乎是神奇的-记住声明它的上下文以及所有可用变量在这种情况下。
如果函数不使用自由变量,则不会形成闭包。
如果还有另一个使用自由变量的内部级别-所有以前的级别都保存词法环境(末尾为示例)
python <3.X中的函数属性
func_closure
或python> 3.X中的__closure__
保存自由变量。python中的每个函数都具有此闭包属性,但如果没有自由变量,则不会保存任何内容。
示例:闭包属性但内部没有内容因为没有自由变量。
>>> def foo():
... def fii():
... pass
... return fii
...
>>> f = foo()
>>> f.func_closure
>>> 'func_closure' in dir(f)
True
>>>
NB:自由变量必须创建一个闭包。
我将使用相同的代码段进行解释。如上:
>>> def make_printer(msg):
... def printer():
... print msg
... return printer
...
>>> printer = make_printer('Foo!')
>>> printer() #Output: Foo!
所有Python函数都具有闭包属性,因此让我们检查与闭包函数关联的封装变量。
这里是函数
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
,它将引用所有保存免费变量,因此我们得到I am free variable
>>> inn('variable')
'I am free variable'
>>>
评论
在Python 3中,func_closure现在称为__closure__,类似于其他各种func_ *属性。
–lvc
2014年1月3日下午6:53
另外__closure_在Python 2.6+中可用,以与Python 3兼容。
–皮埃尔
2014年4月21日在10:27
关闭是指存储附加到函数对象的封闭变量的记录。不是函数本身。在Python中,是__closure__对象才是闭包。
–马丁·彼得斯(Martijn Pieters)♦
18年8月12日在13:40
感谢@MartijnPieters的澄清。
–詹姆斯
18年8月14日在17:42
#3 楼
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提供语法糖的函数装饰器模式。
Update
如前所述,有很多方法可以实现处理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
评论
有一些解决方法。在Python2中,可以在外部作用域中使x = [0],而在内部作用域中使用x [0] + = 1。在Python3中,您可以按原样保留代码,并使用nonlocal关键字。
–unutbu
2014年5月9日上午10:26
“虽然内部函数可以读取外部函数的变量,但不能写入它们。” -根据unutbu的评论,这是不正确的。问题在于,当Python遇到x = ...之类的东西时,x会被解释为局部变量,这当然在那时还没有定义。 OTOH,如果x是使用可变方法的可变对象,则可以很好地对其进行修改,例如如果x是支持inc()方法进行自身变异的对象,则x.inc()将正常工作。
–感谢DK
2015年1月9日在7:14
@ThanhDK并不意味着您无法写入变量?当使用从可变对象调用方法时,您只是在告诉它对其进行修改,而实际上并不是在修改变量(该变量仅包含对该对象的引用)。换句话说,即使您调用inc()或其他函数,变量x指向的引用也将完全相同,并且您没有有效地写入变量。
–user193130
15年1月15日在20:36
还有另一种方法,使x成为counter的属性,严格优于#2 imv。
–orome
2015年11月12日下午13:39
Python 3具有nonlocal关键字,它类似于global,但用于外部函数的变量。这将允许内部函数从其外部函数重新绑定名称。我认为“绑定名称”比“修改变量”更准确。
–leewz
16年1月13日在2:04
#4 楼
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
关键字将内部函数绑定到显式提及的外部变量,实际上将其括起来。因此,更明确地说是“关闭”。评论
有趣,供参考:docs.python.org/3/reference/…。我不知道为什么在python3文档中找到有关闭包的更多信息(以及来自JS的闭包表现如何)并不容易吗?
–user3773048
18/12/30在21:30
#5 楼
我遇到的情况是我需要一个单独的但持久的名称空间。我使用了类。我不是这样。
分隔但持久的名称是闭包。
>>> 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
#6 楼
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
这是一个闭包是什么以及如何使用的示例。
#7 楼
人们对什么是封闭感到困惑。闭包不是内部功能。关闭的含义是关闭行为。因此,内部函数正在关闭称为自由变量的非局部变量。def counter_in(initial_value=0):
# initial_value is the free variable
def inc(increment=1):
nonlocal initial_value
initial_value += increment
return print(initial_value)
return inc
调用
counter_in()
时,将返回具有自由变量inc
的initial_value
函数。因此,我们创建了一个CLOSURE。人们称inc
为闭包函数,我认为这令人困惑,人们认为“好的内部函数就是闭包”。实际上,inc
不是闭包,因为它是闭包的一部分,为了使生活更轻松,所以将其称为闭包函数。 myClosingOverFunc=counter_in(2)
此函数返回
inc
函数,该函数正在对自由变量initial_value
进行闭包。当您调用myClosingOverFunc
myClosingOverFunc()
时,它将打印2。
当python看到闭包系统存在时,它将创建一个名为CELL的新obj。在这种情况下,它将仅存储自由变量的名称,即
initial_value
。此单元格obj将指向另一个存储initial_value
值的对象。在我们的示例中,外部函数和内部函数中的initial_value
指向此单元格对象,并且此单元格对象将指向initial_value
的值。 counter_in
。 variable initial_value =====>> CELL ==========>> value of initial_value
因此,当您调用
initial_value
时,其作用域已消失,但这并不重要。因为变量initial_value
直接引用了CELL Obj。并间接引用cnt
的值。这就是为什么即使外部函数的作用域消失了,内部函数仍然可以访问自由变量的原因。我要编写一个函数,该函数将一个函数作为arg并返回该函数多少次。被称为。
def counter(fn):
# since cnt is a free var, python will create a cell and this cell will point to the value of cnt
# every time cnt changes, cell will be pointing to the new value
cnt = 0
def inner(*args, **kwargs):
# we cannot modidy cnt with out nonlocal
nonlocal cnt
cnt += 1
print(f'{fn.__name__} has been called {cnt} times')
# we are calling fn indirectly via the closue inner
return fn(*args, **kwargs)
return inner
在此示例中,
inner
是我们的自由变量,而cnt
+ cnt
创建CLOSURE。当python看到此信息时,它将创建一个CELL Obj,而cnt
将始终直接引用此单元格obj,而CELL则将引用内存中存储counter(myFunc)()
值的另一个obj。最初为cnt = 0。 cnt ======>>>> CELL =============> 0
当调用内部函数并传递参数q4312079q时,这将使cnt增加1。因此,我们的引用架构将如下更改:
cnt ======>>>> CELL =============> 1 #first counter(myFunc)()
cnt ======>>>> CELL =============> 2 #second counter(myFunc)()
cnt ======>>>> CELL =============> 3 #third counter(myFunc)()
这只是关闭的一个实例。您可以通过传递另一个函数来创建多个闭包实例
counter(differentFunc)()
,这将创建与上面不同的CELL obj。我们刚刚创建了另一个闭包实例。
cnt ======>> difCELL ========> 1 #first counter(differentFunc)()
cnt ======>> difCELL ========> 2 #secon counter(differentFunc)()
cnt ======>> difCELL ========> 3 #third counter(differentFunc)()
#8 楼
我想在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
关键字明确声明了一个。
评论
有趣的是,一些谷歌搜索者发现我是2006年12月:effbot.org/zone/closure.htm。我不确定,“外部重复项”是否会令人讨厌?PEP 227-静态嵌套合并范围以获取更多信息。