提问人:jfs 提问时间:9/27/2008 最后编辑:Communityjfs 更新时间:6/3/2014 访问量:8664
与语言 X 闭包相比,Python 中的闭包有哪些限制?
What limitations have closures in Python compared to language X closures?
问:
其中 X 是任何支持某种闭包的编程语言(C#、Javascript、Lisp、Perl、Ruby 等)。
Python 中的闭包中提到了一些限制(与 Ruby 的闭包相比),但这篇文章已经很旧了,许多限制在现代 Python 中不再存在。
查看具体限制的代码示例会很棒。
相关问题:
答:
我看到人们在使用 Python 时遇到的唯一困难是,当他们试图将变量重赋等非功能性功能与闭包混合在一起时,当这不起作用时会感到惊讶:
def outer ():
x = 1
def inner ():
print x
x = 2
return inner
outer () ()
通常,只要指出一个函数有自己的局部变量就足以阻止这种愚蠢。
评论
目前,最重要的限制是不能赋值给外域变量。换言之,闭包是只读的:
>>> def outer(x):
... def inner_reads():
... # Will return outer's 'x'.
... return x
... def inner_writes(y):
... # Will assign to a local 'x', not the outer 'x'
... x = y
... def inner_error(y):
... # Will produce an error: 'x' is local because of the assignment,
... # but we use it before it is assigned to.
... tmp = x
... x = y
... return tmp
... return inner_reads, inner_writes, inner_error
...
>>> inner_reads, inner_writes, inner_error = outer(5)
>>> inner_reads()
5
>>> inner_writes(10)
>>> inner_reads()
5
>>> inner_error(10)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 11, in inner_error
UnboundLocalError: local variable 'x' referenced before assignment
除非另有声明,否则在本地作用域(函数)中分配给的名称始终是本地名称。虽然有“全局”声明来声明一个变量全局,即使它被赋值给,但对于封闭的变量还没有这样的声明。在 Python 3.0 中,有(将有)“非本地”声明可以做到这一点。
同时,您可以使用可变容器类型来解决此限制:
>>> def outer(x):
... x = [x]
... def inner_reads():
... # Will return outer's x's first (and only) element.
... return x[0]
... def inner_writes(y):
... # Will look up outer's x, then mutate it.
... x[0] = y
... def inner_error(y):
... # Will now work, because 'x' is not assigned to, just referenced.
... tmp = x[0]
... x[0] = y
... return tmp
... return inner_reads, inner_writes, inner_error
...
>>> inner_reads, inner_writes, inner_error = outer(5)
>>> inner_reads()
5
>>> inner_writes(10)
>>> inner_reads()
10
>>> inner_error(15)
10
>>> inner_reads()
15
评论
nonlocal
正如你所提到的,解决了这个问题。一个内部类也可以解决它(但带有列表的版本更简洁)。__call__
@John米利金
def outer():
x = 1 # local to `outer()`
def inner():
x = 2 # local to `inner()`
print(x)
x = 3
return x
def inner2():
nonlocal x
print(x) # local to `outer()`
x = 4 # change `x`, it is not local to `inner2()`
return x
x = 5 # local to `outer()`
return (inner, inner2)
for inner in outer():
print(inner())
# -> 2 3 5 4
在 Python 3 中通过非本地
语句修复:
该语句使列出的标识符引用最近封闭范围(不包括全局变量)中先前绑定的变量。这很重要,因为绑定的默认行为是首先搜索本地命名空间。该语句允许封装的代码重新绑定全局(模块)范围之外的局部范围之外的变量。
nonlocal
评论
注释 @Kevin Little 的答案以包含代码示例
nonlocal
在 python3.0 上不能完全解决这个问题:
x = 0 # global x
def outer():
x = 1 # local to `outer`
def inner():
global x
x = 2 # change global
print(x)
x = 3 # change global
return x
def inner2():
## nonlocal x # can't use `nonlocal` here
print(x) # prints global
## x = 4 # can't change `x` here
return x
x = 5
return (inner, inner2)
for inner in outer():
print(inner())
# -> 2 3 3 3
另一方面:
x = 0
def outer():
x = 1 # local to `outer`
def inner():
## global x
x = 2
print(x) # local to `inner`
x = 3
return x
def inner2():
nonlocal x
print(x)
x = 4 # local to `outer`
return x
x = 5
return (inner, inner2)
for inner in outer():
print(inner())
# -> 2 3 5 4
它适用于 python3.1-3.3
评论
global x
nonlocal x
SyntaxError: no binding for nonlocal 'x' found
nonlocal
在 3.0 之前,更好的解决方法是将变量作为默认参数包含在随附的函数定义中:
def f() x = 5 def g(y, z, x=x): x = x + 1
评论
x
5
与 Javascript 闭包相比,Python 闭包的一个局限性(或“限制”)是它不能用于有效的数据隐藏
Java脚本
var mksecretmaker = function(){
var secrets = [];
var mksecret = function() {
secrets.push(Math.random())
}
return mksecret
}
var secretmaker = mksecretmaker();
secretmaker(); secretmaker()
// privately generated secret number list
// is practically inaccessible
蟒
import random
def mksecretmaker():
secrets = []
def mksecret():
secrets.append(random.random())
return mksecret
secretmaker = mksecretmaker()
secretmaker(); secretmaker()
# "secrets" are easily accessible,
# it's difficult to hide something in Python:
secretmaker.__closure__[0].cell_contents # -> e.g. [0.680752847190161, 0.9068475951742101]
评论