与语言X闭包相比,Python中的闭包有哪些限制?

时间:2008-09-26 20:06:42

标签: python closures

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

Closures in Python中提到了一些限制(与Ruby的闭包相比),但文章很旧,现代Python中不再存在许多限制。

查看具体限制的代码示例会很棒。

相关问题

7 个答案:

答案 0 :(得分:44)

目前,最重要的限制是您无法分配外部范围变量。换句话说,闭包是只读的:

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

答案 1 :(得分:6)

我见过的人遇到的唯一困难就是当他们尝试将非功能性功能(如变量重新分配和闭包)混合在一起时,如果这不起作用,我会感到惊讶:

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

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

答案 2 :(得分:6)

与Javascript闭包相比,Python闭包的限制(或“限制”)是不能用于有效的数据隐藏

的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 # -> e.g. [0.680752847190161, 0.9068475951742101]

答案 3 :(得分:4)

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

  

nonlocal语句使列出的标识符引用最近的封闭范围中除了全局变量之前绑定的变量。这很重要,因为绑定的默认行为是首先搜索本地名称空间。除了全局(模块)范围之外,该语句允许封装代码重新绑定局部范围之外的变量。

答案 4 :(得分:2)

@John Millikin

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

答案 5 :(得分:2)

评论@Kevin Little's answer以包含代码示例

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

答案 6 :(得分:-1)

直到3.0的更好的解决方法是将变量作为默认参数包含在封闭的函数定义中:

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