考虑这个(相当无意义的)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
换句话说,如何在没有明确提及范围的情况下编写一个“知道”变量的闭包?
答案 0 :(得分:5)
这可能是最接近确切等价的东西:
def make_closure():
x = 123
l = locals()
return lambda(varname): eval(varname, None, l)
closure = make_closure()
print closure("x") # 123
最终,你的问题不在于你的本地闭包没有捕获x,而是你的本地范围没有被传递到eval。
有关评估功能的详细信息,请参阅http://docs.python.org/library/functions.html#eval。
不言而喻,这不是一个非常Pythonic的事情。你几乎从来没有真正想在Python中调用eval;如果您认为自己这样做,请退后一步,重新考虑您的大型设计。但是当你确实需要使用它时,你需要了解它与Javascript eval的不同之处。它最终更强大 - 它可以用来模拟动态范围,或者作为exec的简化版本,或者在任意构造的环境中评估代码 - 但这也意味着它更棘手。
答案 1 :(得分:2)
人们已经提到使用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
这有明显的缺点,要求您明确标记要关闭和引用的外部范围中的变量。
答案 2 :(得分:1)
您可以在Python中关闭变量,大约如何在Javascript中使用
def foo():
x = 123
return lambda y: x+y
q = foo()
print q(5)
但是,存在一些差异 - 例如,您无法写入父级范围中的变量(尝试编写只是创建一个本地范围的变量)。此外,Python的eval()
不包括父作用域。
你可以通过明确地从父作用域中获取本地作为并将它们作为本地参数传递给子句eval()
来绕过eval()
,但这里真正的问题是为什么你需要这样做此?
答案 3 :(得分:0)
在Python 2.x中,这需要一些大规模的克服。但是,在Python 3.x中引入了'nonlocal'关键字,以允许访问紧邻的范围内的变量。
可能值得查看此处以获取更多信息:nonlocal keyword in Python 2.x