我在Python中见过并使用过嵌套函数,它们与闭包的定义相匹配。那么为什么它们被称为“嵌套函数”而不是“闭包”呢?
嵌套函数不是闭包,因为它们不被外部使用吗?
更新:我正在阅读关于闭包的文章,它让我想到了关于Python的这个概念。我搜索了下面评论中有人提到的文章,但我不能完全理解文章中的解释,所以我才问这个问题。
我在Python中见过并使用过嵌套函数,它们与闭包的定义相匹配。那么为什么它们被称为“嵌套函数”而不是“闭包”呢?
嵌套函数不是闭包,因为它们不被外部使用吗?
更新:我正在阅读关于闭包的文章,它让我想到了关于Python的这个概念。我搜索了下面评论中有人提到的文章,但我不能完全理解文章中的解释,所以我才问这个问题。
当前回答
我想提供另一个简单的比较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关键字声明一个。
其他回答
我遇到了这样一种情况,需要一个单独但持久的名称空间。 我使用课堂。否则我不会。 隔离但持久的名称是闭包。
>>> 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 counter_in(initial_value=0):
# initial_value is the free variable
def inc(increment=1):
nonlocal initial_value
initial_value += increment
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。这个Cell obj将指向另一个存储initial_value值的对象。
在我们的例子中,外层函数和内部函数中的initial_value将指向这个单元格对象,而这个单元格对象将指向initial_value的值。
variable initial_value =====>> CELL ==========>> value of initial_value
所以当你调用counter_in时,它的作用域消失了,但这无关紧要。因为变量initial_value直接引用CELL对象。它间接引用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是我们的自由变量,inner + cnt创建CLOSURE。当python看到这个时,它将创建一个CELL Obj, cnt将总是直接引用这个CELL Obj,而CELL将引用内存中存储cnt值的另一个Obj。最初问= 0。
cnt ======>>>> CELL =============> 0
当你通过传递参数计数器(myFunc)()调用内部函数时,这将使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)()
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()
another = closure() # another instance, with a different stack
start() # prints 1
start() # prints 2
another() # print 1
start() # prints 3
nonlocal关键字将内部函数绑定到显式提到的外部变量,实际上将其封闭起来。因此更明确的是一个“闭包”。
这里提供了一种通过代码对象识别函数是否是闭包的方法。
正如在其他回答中已经提到的,并不是每个嵌套函数都是闭包。给定一个复合函数(表示整个动作),它的中间状态可以是闭包或嵌套函数。 闭包是一种由其(非空的)封闭范围(自由变量空间)“参数化”的函数。注意,复合函数可以由这两种类型组成。
(Python的)内部类型代码 对象表示编译后的函数体。它的属性co_cellvars和co_freevars可用于“查看”函数的闭包/作用域。 正如文件中提到的
Co_freevars:自由变量名的元组(通过函数的闭包引用) Co_cellvars:单元格变量名的元组(由包含作用域引用)。
一旦函数被读取,通过递归调用返回一个局部函数,它带有自己的__closure__(因此是cell_contents)和一个来自它的clousre和作用域的自由变量列表。
让我们介绍一些支持函数
# the "lookarounds"
def free_vars_from_closure_of(f):
print(f.__name__, 'free vars from its closure', f.__code__.co_cellvars)
def free_vars_in_scopes_of(f):
print(f.__name__, 'free vars in its scope ', f.__code__.co_freevars)
# read cells values
def cell_content(f):
if f.__closure__ is not None:
if len(f.__closure__) == 1: # otherwise problem with join
c = f.__closure__[0].cell_contents
else:
c = ','.join(str(c.cell_contents) for c in f.__closure__)
else:
c = None
print(f'cells of {f.__name__}: {c}')
这里有一个例子,来自另一个用更系统的方式重写的答案
def f1(x1):
def f2(x2):
a = 'free' # <- better choose different identifier to avoid confusion
def f3(x3):
return '%s %s %s %s' % (x1, x2, a, x3)
return f3
return f2
# partial functions
p1 = f1('I')
p2 = p1('am')
# lookaround
for p in (f1, p1, p2):
free_vars_in_scopes_of(p)
free_vars_from_closure_of(p)
cell_content(p)
输出
f1 free vars in its scope () # <- because it's the most outer function
f1 free vars from its closure ('x1',)
cells of f1: None
f2 free vars in its scope ('x1',)
f2 free vars from its closure ('a', 'x2')
cells of f2: I
f3 free vars in its scope ('a', 'x1', 'x2')
f3 free vars from its closure () # <- because it's the most inner function
cells of f3: free, I, am
对应的lambda:
def g1(x1):
return lambda x2, a='free': lambda x3: '%s %s %s %s' % (x1, x2, a, x3)
从自由变量/范围的角度来看是等价的。唯一微小的区别是code对象的某些属性的一些值: Co_varnames, co_consts, co_code, co_lnotab, co_stacksize…当然还有__name__属性。
一个混合的例子,闭包和不立即:
# example: counter
def h1(): # <- not a closure
c = 0
def h2(c=c): # <- not a closure
def h3(x): # <- closure
def h4(): # <- closure
nonlocal c
c += 1
print(c)
return h4
return h3
return h2
# partial functions
p1 = h1()
p2 = p1()
p3 = p2('X')
p1() # do nothing
p2('X') # do nothing
p2('X') # do nothing
p3() # +=1
p3() # +=1
p3() # +=1
# lookaround
for p in (h1, p1, p2, p3):
free_vars_in_scopes_of(p)
#free_vars_from_closure_of(p)
cell_content(p)
输出
1 X
2 X
3 X
h1 free vars in its scope ()
cells of h1: None
h2 free vars in its scope ()
cells of h2: None
h3 free vars in its scope ('c',)
cells of h3: 3
h4 free vars in its scope ('c', 'x')
cells of h4: 3,X
H1和h2都不是闭包,因为它们在作用域内没有单元格和自由变量。 H3和H3是闭包,共享(在本例中)相同的单元格和c的自由变量。h4有一个带有自己单元格的自由变量x。
最后的考虑:
__closure__属性和__code__。Co_freevars可用于检查自由变量的值和名称(标识符) nonlocal和__code__之间的反类比(在广义上)。Co_cellvars:非局部作用于外部函数__code__。Co_cellvars改为内部函数