Почему я получаю "NameError: имя не определено" с 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 ответ

Вы не делаете "точно то же самое" в своей программе. Точный код, дословно скопированный в файл и работающий как скрипт 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 исключение, отмеченное в вашем вопросе, потому что (цитируя документы),

Во всех случаях, если опциональные части опущены, код выполняется в текущей области видимости.

Так как код выше импортирует 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() будет работать, потому что тогда вы явно импортируете random в глобальном масштабе, где все могут видеть это.

Другие вопросы по тегам