为什么eval函数中的lambda无法闭合用户定义的“locals”字典中的变量?

4
我希望使用内置的eval函数来评估一个lambda表达式,其中变量y在'locals'参数中定义。遗憾的是结果函数不起作用:
>>>x = eval('lambda: print(y)',{},{'y':2})
>>>x()
Traceback (most recent call last):
  File "<pyshell#75>", line 1, in <module>
    x()
  File "<string>", line 1, in <lambda>
NameError: name 'y' is not defined

但是如果在“globals”参数中定义了y,它就可以工作:

>>> x = eval('lambda: print(y)', {'y': 2},{})
>>> x()
2

据我所知,lambda表达式应该捕获整个当前帧,包括在globals和locals参数中定义的所有变量。
那么为什么Python会表现出这样的行为呢?

你知道 eval 的危险吗? - bruno desthuilliers
2个回答

3
简单来说:传递一个填充的“locals”目录并不会改变Python解析函数代码并决定哪些名称是本地名称,哪些是全局名称的方式。
本地名称是参数名称和在函数体内绑定而未显式声明为全局或非本地名称的名称。在这里,“y”不是参数,并且没有在函数体内绑定(在lambda中是不可能的),因此编译器将其标记为全局名称。
现在,这些全局和本地环境是用于评估表达式本身的(在这里是完整的“lambda: print(y)”表达式),而不是“lambda主体的本地环境”,因此即使有一种方法可以使“y”成为函数主体的本地环境(提示:有一种方法-但它无法解决您的问题),该名称仍然不会自动设置为传递给“eval”的本地字典中的“y”值。
但实际上这并不是问题-通过eval("lambda: y", {"y":42})创建的函数捕获了传递给eval的全局字典,并在模块/脚本/任何全局变量的位置使用它,因此它将按预期工作:
Python 3.4.3 (default, Nov 28 2017, 16:41:13) 
[GCC 4.8.4] on linux
>>> f = eval("lambda: y+2", {'y':2}, {})
>>> f()
4
>>> y = 42
>>> f()
4

现在你已经有了解释,我想补充一点的是eval()非常危险,通常情况下有更好的解决方案,具体取决于你试图解决的问题。由于你没有提供任何上下文信息,所以无法提供更多信息,但我必须说,我很难想象出像f = eval("lambda: whatever")这样的具体用例。

-1

x = eval('lambda: print(y)', {},{'y':2}) 不等于这行 x = eval('lambda: print(y)', {'y': 2},{}) 在第一部分改变参数顺序就可以工作了。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接