为什么我通过exec()收到“ NameError:名称未定义”?

时间:2019-02-23 09:42:16

标签: python python-3.x scope python-import python-exec

当我在控制台(在PyCharm中)中尝试此代码时:

exec("import random")
exec("def f():\n\treturn random.randint(0, 10), random.randint(0, 10)")
locals()['f']()

它工作正常。但是,当我尝试在程序中执行完全相同的操作时,它不起作用,并且出现异常

NameError: name 'random' is not defined.

我发现这段代码不会引发错误:

exec("import random", globals(), globals())
exec("def f():\n\treturn random.randint(0, 10), random.randint(0, 10)", globals(), globals())
globals()['f']()

但是我不明白为什么。

这是怎么回事?

1 个答案:

答案 0 :(得分:1)

您没有在程序中执行“完全相同”的操作。将该精确的代码逐字复制到文件中并作为Python脚本运行,可以正常工作(尽管没有可见结果)。

我认为您可能实际上正在做的事情是这样的:

def import_stuff():
    exec("import random")

def do_stuff():
    import_stuff()
    exec("def f():\n\treturn random.randint(0, 10), random.randint(0, 10)")
    locals()['f']()

do_stuff()

上面的代码确实导致您的问题中提到了NameError异常,因为(引述docs),

  

在所有情况下,如果省略了可选部分,则代码将在当前作用域中执行。

由于上面的代码将random导入到import_stuff()的本地范围中,因此do_stuff()不可见。

实际上,上面的代码在行为上与以下代码相同:

def import_stuff():
    import random

def do_stuff():
    import_stuff()
    def f():
        return random.randint(0, 10), random.randint(0, 10)
    f()

do_stuff()

...出于同样的原因也失败了。

假设这是实际代码中真正发生的事情,那么可以通过在globals(), globals()中添加exec()自变量来修改您的问题,因为您要显式导入{{1} }进入全局范围,一切都可以看到。