Почему Python 3 exec() терпит неудачу при указании локальных пользователей?

В Python 3 выполняется следующее:

code = """
import math

def func(x):
    return math.sin(x)

func(10)
"""
_globals = {}
exec(code, _globals)

Но если я попытаюсь также захватить локальную переменную dict, она терпит неудачу с NameError:

>>> _globals, _locals = {}, {}
>>> exec(code, _globals, _locals)
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-9-aeda81bf0af1> in <module>()
----> 1 exec(code, {}, {})

<string> in <module>()

<string> in func(x)

NameError: name 'math' is not defined

Почему это происходит, и как я могу выполнить этот код при захвате как глобальных, так и локальных переменных?

Ответ 1

Из exec() документации:

Помните, что на уровне модуля глобальные и локальные языки являются одним и тем же словарем. Если exec получает два отдельных объекта как глобальные и локальные, код будет выполнен так, как если бы он был встроен в определение класса.

Вы прошли в двух отдельных словарях, но попытались выполнить код, который требует наличия глобальных глобальных возможностей. import math в классе создаст атрибут локальной области, и создаваемая вами функция не сможет получить доступ к этому, поскольку имена классов классов не рассматриваются для закрытия функций.

См. Именование и привязка в ссылке на модель исполнения Python:

Блоки и аргументы определения класса exec() и eval() являются особыми в контексте разрешения имен. Определение класса - это исполняемый оператор, который может использовать и определять имена. Эти ссылки следуют нормальным правилам разрешения имен, за исключением того, что в глобальном пространстве имен просматриваются несвязанные локальные переменные. Пространство имен определения класса становится атрибутом словаря класса. Объем имен, определенных в блоке класса, ограничен блоком класса; он не распространяется на кодовые блоки методов [.]

Вы можете воспроизвести ошибку, выполнив код в определении класса:

>>> class Demo:
...     import math
...     def func(x):
...         return math.sin(x)
...     func(10)
...
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 5, in Demo
  File "<stdin>", line 4, in func
NameError: name 'math' is not defined

Просто перейдите в один словарь.