eval 在列表理解中失败

cbs*_*teh 4 python eval list-comprehension python-3.x

考虑以下假设代码:

class B(object):
    def __init__(self):
        self.b = 2

    def foo(self):
        out1 = [eval('self.b')]    # ok
        print(out1)                # prints: [2]
        out2 = [eval(cmd) for cmd in ['self.b']]    # fails
        print(out2)    # NameError: name 'self' is not defined

b = B()
b.foo()
Run Code Online (Sandbox Code Playgroud)

为什么语句是out1ok 而不是 for out2,这会给出错误“'self' is not defined”?

我正在学习 Python,在试验eval. 是的,我知道eval在这个例子中使用 是不合适的,但只是为了从表面上看这个例子,有人可以解释为什么语句 forout2给出错误信息吗?似乎两个语句都应该起作用并给出相同的结果。

感谢您的任何指导。

Wil*_*sem 6

通过使用列表理解,您实际上定义了一个新的 scope。事实上,如果我们将列表理解改为:

out2 = [print(globals()) or print(locals()) or eval(cmd) for cmd in ['self.b']]
Run Code Online (Sandbox Code Playgroud)

我们强制 Python 在eval(..)调用之前打印局部和全局变量,我们得到如下结果:

{'__builtins__': <module 'builtins' (built-in)>, '__name__': '__main__', '__loader__': <class '_frozen_importlib.BuiltinImporter'>, 'b': <__main__.B object at 0x7f406f55d4a8>, '__doc__': None, '__package__': None, 'B': <class '__main__.B'>, '__spec__': None}
{'.0': <list_iterator object at 0x7f406f55df28>, 'cmd': 'self.b'}
Run Code Online (Sandbox Code Playgroud)

所以作为局部变量,我们只有 a.0和 a cmd

但是,您可以self使用以下方法传递到列表理解:

globs = globals()
locs = locals()
out2 = [eval(cmd,globs,locs) for cmd in ['self.b']]
Run Code Online (Sandbox Code Playgroud)

所以现在eval(..)使用在函数作用域中定义的局部和全局变量。由于我们使用locsglobs。Python 会将这些字典的引用传递给eval(..)调用。

最后,每次使用eval(..): eval时都会发出警告,这是一个危险的函数。你最好只在你真的需要它的时候使用它。

该附加范围(在引入额外的副作用)是该循环变量确实没有泄漏:列表解析后cmd的清理:你不能再访问它(通常它会保持它的最后一个元素处理)。例如:

>>> [x for x in range(10)]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> x
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'x' is not defined
Run Code Online (Sandbox Code Playgroud)