Вы можете увидеть сгенерированный байт-код для каждой версии Python:
>>> from dis import dis
А для каждого переводчика:
#Python 3.2
>>> dis(Testing.__init__)
...
5 10 LOAD_GLOBAL 1 (a_func)
...
#Python 2.7
>>> dis(Testing.__init__)
...
5 8 LOAD_NAME 0 (a_func)
...
Как видите, Python 3.2 ищет глобальное значение (LOAD_GLOBAL) с именем a_func
, а 2.7 сначала ищет локальную область (LOAD_NAME) перед поиском глобальной.
Если вы выполните print(locals())
после exec
, вы увидите, что a_func
создается внутри функции __init__
.
Я действительно не знаю, почему это так, но, похоже, изменилось то, как таблицы символов обрабатываются.
Кстати, если вы хотите создать a_func = None
поверх вашего __init__
метода, чтобы интерпретатор знал, что это локальная переменная, он не будет работать, поскольку байт-код теперь будет LOAD_FAST
, а это не сделать поиск, но напрямую получает значение из списка.
Единственное решение, которое я вижу, - это добавить globals()
в качестве второго аргумента к exec
, чтобы создать a_func
как глобальную функцию, к которой может получить доступ код операции LOAD_GLOBAL
.
Редактировать
Если вы удалите оператор exec
, Python2.7 изменит байт-код с LOAD_NAME
на LOAD_GLOBAL
. Поэтому, используя exec
, ваш код всегда будет медленнее на Python2.x, потому что он должен искать изменения в локальной области.
Поскольку Python3 exec
не является ключевым словом, интерпретатор не может быть уверен, что он действительно выполняет новый код или делает что-то еще ... Так что байт-код не меняется.
1044 * Е.Г. *
>>> exec = len
>>> exec([1,2,3])
3
Т.Л., др
exec('...', globals())
может решить проблему, если вам все равно, какой результат будет добавлен в глобальное пространство имен