eval在列表理解中失败

时间:2017-07-19 15:26:50

标签: python python-3.x eval list-comprehension

考虑以下假设代码:

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()

为什么out1的陈述是正确的,而out2的陈述却没有定义错误“'self'?”

我正在学习Python,我在尝试eval时遇到了这个问题。是的,我知道在这个例子中使用eval是不合适的,但只是为了以表面价值来看这个例子,有人可以解释为什么out2的语句会给出错误信息吗?似乎两种陈述都应该起作用并给出相同的结果。

感谢您提供任何指导。

1 个答案:

答案 0 :(得分:2)

通过使用列表推导,您实际上定义新范围。确实,如果我们将列表理解改为:

out2 = [print(globals()) or print(locals()) or eval(cmd) for cmd in ['self.b']]

我们强制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'}

因此,作为局部变量,我们只有.0cmd

但是,您可以使用以下命令将self传递给列表解析:

globs = globals()
locs = locals()
out2 = [eval(cmd,globs,locs) for cmd in ['self.b']]

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

最后一个警告就像每次使用eval(..):eval一样是一个危险的功能。只有在你真的需要的时候才能更好地使用它。

此附加范围的另一个副作用(在中引入)是循环变量 泄漏:列表理解后{{1清理:你不能再访问它(通常它会保存它处理的最后一个元素)。例如:

cmd