在闭包中自动绑定范围变量



考虑这个(相当毫无意义的)JavaScript代码:

function make_closure() {
    var x = 123, y = 456;
    return function(varname) { return eval(varname) }
}
closure = make_closure()
closure("x") // 123
closure("y") // 456

make_closure 返回的函数不包含对作用域变量的任何引用,但在调用时仍能够返回其值。

有没有办法在 python 中做同样的事情?

def make_closure():
    x = 123
    return lambda varname: ...no "x" here...
closure = make_closure()
print closure("x") # 123

换句话说,如何编写一个闭包,在定义范围时"知道"变量而不明确提及它们?

这可能是最接近完全相同的东西:

def make_closure():
  x = 123
  l = locals()
  return lambda(varname): eval(varname, None, l)
closure = make_closure()
print closure("x") # 123

归根结底,你的问题不在于 x 没有被本地闭包捕获,而在于你的本地范围没有传递到 eval 中。

有关 eval 函数的详细信息,请参阅 http://docs.python.org/library/functions.html#eval。

不言而喻,这不是一件非常 Python 的事情。你几乎从来没有想过在 Python 中调用 eval;如果您认为这样做,请退后一步,重新考虑您的更大设计。但是当你确实需要使用它时,你需要了解它与Javascript eval有什么不同。它最终功能更强大——它可以用来模拟动态作用域,或者作为 exec 的简化版本,或者在任何任意构造的环境中评估代码——但这也意味着它更棘手。

人们已经提到使用locals() .另一种不太灵活的方法是在Python 3中使用新的nonlocal关键字:

>>> def make_closure():
...     x = 123
...     def return_var(varname):
...         nonlocal x
...         return eval(varname)
...     return return_var
... 
>>> closure = make_closure()
>>> closure("x")
123

这有一个明显的缺点,即要求您显式标记要关闭和引用的外部作用域中的变量。

你可以在 Python 中关闭变量,就像在 Javascript 中一样:

def foo():
  x = 123
  return lambda y: x+y
q = foo()
print q(5)

http://ideone.com/Q8iBg

但是,存在一些差异 - 例如,您无法写入父级范围内的变量(尝试写入只会创建一个局部范围的变量)。此外,Python 的eval()不包括父作用域。

您可以通过从父范围显式获取局部变量并将其作为其局部变量参数传递到子级的eval()中来绕过eval(),但这里真正的问题是为什么需要这样做?

在Python 2.x中,这需要大量的清理。但是,在Python 3.x中引入了"nonlocal"关键字,以允许访问紧邻范围内的变量。

可能值得在这里查看更多信息:Python 2.x 中的非本地关键字

最新更新