Python闭包相比于语言X的闭包有什么限制?

48 投票
7 回答
8714 浏览
提问于 2025-04-11 09:27

X可以是任何一种编程语言(比如C#、Javascript、Lisp、Perl、Ruby、Scheme等),只要它支持某种形式的闭包。

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

如果能看到一个具体限制的代码示例,那就太好了。

相关问题

7 个回答

6

Python 的闭包有一个限制,跟 Javascript 的闭包比起来,就是它不能有效地实现数据隐藏

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

我看到的关于Python的一个常见问题,就是有些人试图把一些不太符合函数特性的东西,比如重新给变量赋值,和闭包(closure)混在一起用,结果发现这样不太好使,感到很惊讶:

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

通常,只要提醒一下函数有自己的局部变量,这样的错误就能避免了。

45

目前最重要的限制是,你不能给外部作用域的变量赋值。换句话说,闭包中的变量是只读的:

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

在一个局部作用域(比如一个函数)中被赋值的变量,默认都是局部的,除非你特别声明。虽然有一个叫做'global'的声明,可以让你把变量声明为全局的,即使它被赋值了,但对于封闭变量(即在闭包中使用的变量)来说,目前还没有这样的声明。不过在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

撰写回答