Ответ 1
Вы можете увидеть сгенерированный байт-код для каждой версии 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__
.
Я действительно не знаю, почему это так, но, похоже, это изменение в отношении таблиц символов.
BTW, если вы хотите создать a_func = None
поверх своего метода __init__
, чтобы интерпретатор знал его локальную переменную, он не будет работать, так как байт-код теперь будет LOAD_FAST
, и это не будет выполните поиск, но сразу получите значение из списка.
Единственное решение, которое я вижу, - добавить globals()
в качестве второго аргумента в exec
, чтобы создать a_func
в качестве глобальной функции, к которой может быть доступен код операции LOAD_GLOBAL
.
Edit
Если вы удалите оператор exec
, Python2.7 измените байт-код с LOAD_NAME
на LOAD_GLOBAL
. Таким образом, используя exec
, ваш код будет всегда медленнее на Python2.x, потому что он должен искать локальную область для изменений.
Поскольку Python3 exec
не является ключевым словом, интерпретатор не может быть уверен, действительно ли он выполняет новый код или делает что-то еще... Так что байт-код не изменяется.
например.
>>> exec = len
>>> exec([1,2,3])
3
TL;DR
exec('...', globals())
может решить проблему, если вам не все равно, что результат добавляется в глобальное пространство имен