与X语言closures相比,Python有什么限制?

其中X是支持某些闭包的编程语言(C#,Javascript,Lisp,Perl,Ruby,Scheme等)。

Python中的闭包 (与Ruby的闭包相比)中提到了一些限制,但是这篇文章比较陈旧,在现代Python中还不存在很多限制。

看到一个具体限制的代码示例将是很好的。

相关问题

  • 你能解释闭包(与Python有关)吗?
  • 什么是“closures”?
  • javascriptclosures如何工作?

目前最重要的限制是你不能分配给外部variables。 换句话说,闭包是只读的:

>>> 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 

除非另外声明,否则在本地范围(函数)中分配的名称始终是本地名称。 虽然有“全局”声明来声明一个variables的全局variables,即使它被赋值,也没有这样的封闭variables的声明。 在Python 3.0中,有(将是)“nonlocal”声明就是这样做的。

您可以通过使用可变的容器types来同时解决此限制:

 >>> 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 

我看到人们遇到的唯一困难就是当他们试图将非function特性(如variables重新分配和闭包)混合在一起时,并且在这种情况下无法工作时感到惊讶:

 def outer (): x = 1 def inner (): print x x = 2 return inner outer () () 

通常只是指出一个函数有自己的局部variables就足以阻止这种愚蠢的行为。

与Javascriptclosures相比,Pythonclosures的一个限制(或“限制”)是它不能用于有效的数据隐藏

使用Javascript

 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 

python

 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 # -> eg [0.680752847190161, 0.9068475951742101] 

在Python 3中通过nonlocal语句修复:

nonlocal语句会导致列出的标识符引用最近包含范围(不包括全局variables)的先前绑定variables。 这很重要,因为绑定的默认行为是首先search本地名称空间。 该语句允许封装代码除了全局(模块)作用域之外还重新绑定局部作用域之外的variables。

约翰·米利肯

 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 

@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

直到3.0的更好的解决方法是将variables作为缺省参数包含在所包含的函数定义中:

 def f()
     x = 5
     def g(y,z,x = x):
         x = x + 1