为什么python嵌套函数不称为闭包?
我在Python中见过并使用过嵌套函数,它们与闭包的定义相匹配。那么为什么它们被称为嵌套函数而不是闭包呢 嵌套函数不是闭包,是因为外部世界不使用它们吗为什么python嵌套函数不称为闭包?,python,closures,nested-function,Python,Closures,Nested Function,我在Python中见过并使用过嵌套函数,它们与闭包的定义相匹配。那么为什么它们被称为嵌套函数而不是闭包呢 嵌套函数不是闭包,是因为外部世界不使用它们吗 更新:我在读关于闭包的书,它让我想到了关于Python的这个概念。我在下面的一篇评论中搜索并找到了某人提到的文章,但我无法完全理解该文章中的解释,因此我提出了这个问题。当函数从已完成执行的封闭范围访问局部变量时,就会发生闭包 def make_printer(msg): def printer(): print msg
更新:我在读关于闭包的书,它让我想到了关于Python的这个概念。我在下面的一篇评论中搜索并找到了某人提到的文章,但我无法完全理解该文章中的解释,因此我提出了这个问题。当函数从已完成执行的封闭范围访问局部变量时,就会发生闭包
def make_printer(msg):
def printer():
print msg
return printer
printer = make_printer('Foo!')
printer()
调用make_printer时,将在堆栈上放置一个新帧,打印机函数的编译代码作为常量,msg的值作为本地值。然后它创建并返回函数。因为函数printer引用了msg变量,所以make_printer函数返回后,它将保持活动状态
因此,如果嵌套函数没有
访问封闭作用域的局部变量,
当在该范围外执行时,请执行该操作,
那么它们就不是闭包了
>>> 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 make_printer(msg):
def printer(msg=msg):
print msg
return printer
printer = make_printer("Foo!")
printer() #Output: Foo!
这里,我们将该值绑定到参数的默认值。当创建函数printer时会发生这种情况,因此在make_printer返回后,不需要维护对打印机外部msg值的引用。在此上下文中,msg只是函数printer的一个普通局部变量 这个问题已经有人回答了
然而,有些人可能对变量如何存储在引擎盖下感兴趣
在进入代码段之前:
闭包是从封闭环境继承变量的函数。当您将一个函数回调作为参数传递给另一个将执行I/O的函数时,该回调函数将在稍后被调用,并且该函数将几乎神奇地记住声明它的上下文,以及该上下文中可用的所有变量
如果函数不使用自由变量,它就不会形成闭包
如果有另一个内部级别使用自由变量,那么前面的所有级别都会在末尾保存词法环境示例
python<3.X中的函数属性func_closure或python>3.X中的函数属性func_closure保存自由变量
python中的每个函数都有这个闭包属性,但是如果没有自由变量,它不会保存任何内容
示例:包含闭包属性,但内部没有内容,因为没有自由变量
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
注意:必须创建一个闭包
我将使用与上面相同的片段进行解释:
>>> def make_printer(msg):
... def printer():
... print msg
... return printer
...
>>> printer = make_printer('Foo!')
>>> printer() #Output: Foo!
所有Python函数都有一个closure属性,所以让我们检查与closure函数相关联的封闭变量
myClosingOverFunc=counter_in(2)
下面是函数打印机的属性func_闭包
在上面的输出中,您可以看到单元格内容,让我们看看它存储了什么:
>>> printer.func_closure[0].cell_contents
'Foo!'
>>> type(printer.func_closure[0].cell_contents)
<type 'str'>
>>>
在上面的代码段中,我没有在printer函数中打印msg,因此它不会创建任何自由变量。因为没有自由变量,所以闭包中没有内容。这正是我们上面看到的
现在,我将解释另一个不同的片段,用闭包清除所有自由变量:
在这里,当我们调用inn时,它将引用所有save-free变量,因此我们得到I-am-free变量
给出:
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对闭包的支持很弱。要了解我的意思,请以下面使用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
闭包非常优雅,因为它使这样编写的函数能够拥有内部内存。从Python2.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的范围限制,我将公开一些方法 一,。一般不建议使用全局关键字 二,。在Python3.x中,使用@unutbu和@leewz建议的关键字 三,。定义一个简单的可修改类对象 并在initCounter中创建一个对象范围来存储变量
def initCounter ():
scope = Object()
scope.x = 0
def counter():
scope.x += 1
print scope.x
return counter
由于作用域实际上只是一个引用,使用其字段执行的操作不会真正修改作用域本身,因此不会出现错误
四,。正如@unutbu指出的,另一种方法是将每个变量定义为一个数组x=[0],并修改它的第一个元素x[0]+=1。银
因为x本身没有被修改,所以没有出现错误
五,。正如@raxacoricocfallapatorius所建议的,您可以将x作为计数器的属性
我遇到了一种情况,需要一个单独但持久的名称空间。 我上过课。我不知道其他的。 隔离但持久的名称是闭包
>>> 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
Python2没有闭包——它有类似闭包的变通方法
>>> 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
在已经给出的答案中有很多例子——将变量复制到内部函数,修改内部函数上的对象,等等
在Python 3中,支持更加明确和简洁:
def closure():
count = 0
def inner():
nonlocal count
count += 1
print(count)
return inner
用法:
start = closure()
another = closure() # another instance, with a different stack
start() # prints 1
start() # prints 2
another() # print 1
start() # prints 3
nonlocal关键字将内部函数绑定到显式提到的外部变量,实际上将其包围。因此更明确地说是一个“闭包”。如果这有助于让事情更清楚,我想提供python和JS示例之间的另一个简单比较 JS: 并执行:
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关键字显式地声明一个。人们对闭包是什么感到困惑。闭包不是内部功能。关闭的含义是关闭的行为。所以内部函数在一个称为自由变量的非局部变量上闭合
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
当您在中调用计数器_时,将返回包含自由变量初始_值的inc函数。所以我们创建了一个闭包。人们称inc为闭包函数,我认为这让人困惑,人们认为ok的内部函数是闭包。事实上,inc并不是一个闭包,因为它是闭包的一部分,为了让生活变得简单,他们称之为闭包函数
myClosingOverFunc=counter_in(2)
这将返回inc函数,该函数在自由变量初始值上关闭。调用myClosingOverFunc时
它将打印2
当python看到一个闭包系统存在时,它会创建一个名为CELL的新obj。这将只存储自由变量的名称,在本例中,它是初始值。此单元格obj将指向另一个存储初始_值的对象
在我们的示例中,外部函数和内部函数中的初始_值将指向此单元格对象,而此单元格对象将指向初始_值的值
所以,当你在它的范围内调用counter_时,它就消失了,但这并不重要。因为变量初始值直接引用单元格Obj。它间接引用了初始值的值。这就是为什么即使外部函数的作用域消失了,内部函数仍然可以访问自由变量
假设我想写一个函数,它接受一个函数作为参数,并返回这个函数被调用的次数
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
在这个例子中,cnt是我们的自由变量,内部+cnt创建闭包。当python看到这一点时,它将创建一个单元格Obj,cnt将始终直接引用该单元格Obj,而该单元格将引用内存中存储cnt值的另一个Obj。最初cnt=0
cnt ======>>>> CELL =============> 0
当您通过传递参数countermyFunc调用内部函数时,这将使cnt增加1。因此,我们的引用模式将更改如下:
cnt ======>>>> CELL =============> 1 #first counter(myFunc)()
cnt ======>>>> CELL =============> 2 #second counter(myFunc)()
cnt ======>>>> CELL =============> 3 #third counter(myFunc)()
这只是闭包的一个实例。通过传递另一个函数,可以创建多个闭包实例
counter(differentFunc)()
这将创建与上述不同的单元格obj。我们刚刚创建了另一个闭包实例
cnt ======>> difCELL ========> 1 #first counter(differentFunc)()
cnt ======>> difCELL ========> 2 #secon counter(differentFunc)()
cnt ======>> difCELL ========> 3 #third counter(differentFunc)()
有趣的是,有人在谷歌上找到了我这个日期是2006年12月的:。我不确定外部复制品是不是不受欢迎?更多信息。你的答案比我的好得多,你的观点很好,但是如果我们要使用最严格的函数编程定义,你的例子是函数吗?已经有一段时间了,我不记得严格的函数编程是否允许不返回值的函数。如果你认为返回值是没有的,但是这是另一个主题。MikoBi,我不确定我们是否需要考虑函数编程,因为Python不是真正的函数语言,尽管它确实可以被使用。但是,不,内在功能不是那种意义上的功能,因为它们的全部目的是产生副作用。创建一个函数很容易就能很好地说明这些要点,@mikerobi:一个代码块是否是一个闭包取决于它是否在其环境中关闭,而不是你所称的闭包。它可以是一个例程、函数、过程、方法、块、子例程等等。在Ruby中,方法不能是闭包,只能是块。在Java中,方法不能是闭包,但类可以。这并没有使他们的结局减少。虽然它们只关闭了一些变量,并且不能修改它们,这使得它们几乎毫无用处。你可以说一个方法只是一个封闭在自我之上的过程。在JavaScript/Python中,这几乎是正确的。@J?
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
myClosingOverFunc=counter_in(2)
myClosingOverFunc()
variable initial_value =====>> CELL ==========>> value of initial_value
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
cnt ======>>>> CELL =============> 0
cnt ======>>>> CELL =============> 1 #first counter(myFunc)()
cnt ======>>>> CELL =============> 2 #second counter(myFunc)()
cnt ======>>>> CELL =============> 3 #third counter(myFunc)()
counter(differentFunc)()
cnt ======>> difCELL ========> 1 #first counter(differentFunc)()
cnt ======>> difCELL ========> 2 #secon counter(differentFunc)()
cnt ======>> difCELL ========> 3 #third counter(differentFunc)()