3

考虑这个(相当无意义的)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

换句话说,如何编写一个闭包来“知道”定义范围中的变量而不明确提及它们?

4

4 回答 4

5

这可能是最接近精确等价物的东西:

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

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

于 2012-06-19T22:29:13.537 回答
2

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

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

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

于 2012-06-19T22:35:25.710 回答
1

您可以在 Python 中关闭变量,大致类似于在 Javascript 中的操作方式:

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

q = foo()

print q(5)

http://ideone.com/Q8iBg

但是,存在一些差异 - 例如,您不能写入父范围内的变量(尝试写入只是创建一个局部范围的变量)。此外,Pythoneval()不包括父范围。

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

于 2012-06-19T22:27:08.890 回答
0

在 Python 2.x 中,这需要大量的组合。但是,在 Python 3.x 中,引入了“nonlocal”关键字以允许访问直接封闭范围内的变量。

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

于 2012-06-19T22:34:00.660 回答