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

时间:2020-03-06 14:48:19  来源:igfitidea点击:

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

在Python的闭包中(与Ruby的闭包相比)中提到了一些限制,但是该文章过时了,现代的Python中不再存在许多限制。

看到一个代码示例来了解具体的限制会很棒。

相关问题:

  • 我们能解释闭包(因为它们与Python有关)吗?
  • 什么是封包?
  • javascript闭包如何工作?

解决方案

我见过的人们遇到的唯一困难特别是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

除非另行声明,否则在本地范围(函数)中分配给它的名称始终是本地的。尽管存在"全局"声明来声明变量全局变量(即使已将其分配给变量),但对于封闭变量-yet尚无此类声明。在Python 3.0中,有(将有)" nonlocal"声明可以做到这一点。

我们可以同时使用可变的容器类型来解决此限制:

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

@约翰·米利金(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

在Python 3中通过nonlocal语句进行​​了修复:

The nonlocal statement causes the listed identifiers to refer to previously bound variables in the nearest enclosing scope excluding globals. This is important because the default behavior for binding is to search the local namespace first. The statement allows encapsulated code to rebind variables outside of the local scope besides the global (module) scope.

@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,更好的解决方法是将变量作为默认参数包含在随附的函数定义中:

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