Я пытался воспроизвести ваши шаги, и у меня все работало нормально. Давайте посмотрим на процесс шаг за шагом. Прежде всего, создание кода.
Здесь мы импортируем все, что нам нужно:
from byteplay import Code, LOAD_CONST, STORE_NAME, RETURN_VALUE
Давайте создадим список кодов операций с соответствующими параметрами (список кортежей, каждый из которых содержит код операции в качестве первого элемента и аргумент в качестве второго):
lst = [
(LOAD_CONST, 3),
(STORE_NAME, 'a'),
(LOAD_CONST, None),
(RETURN_VALUE, None)
]
Хорошо, покончим с этим. Далее наиболее ответственный шаг - создание объекта Code. Он получает 10 аргументов . Давайте посмотрим:
x = Code(
lst, # opcodes list (what we execute)
[], # outer scope variables (obviously, we don't have any here),
[], # arguments (nothing here),
False, # *args here? Nope
False, # **kwargs here? Nope
False, # !!!Important!!! DO WE CREATE NEW NAMESPACE? No! We use given!
'', # name ...
'', # filename ... who cares...
0, # first line number
'' # docstring
)
Внимание! Если для 6-го аргумента создания кода задано значение True, вы не получите переменную 'a', сохраненную в локальных системах после выполнения нашего кода. Вот так работают функции. Они создают свое собственное пространство имен (co_names), и пространство имен, в котором мы выполняем код, не будет исправлено переменной 'a'.
Итак, давайте запустим!
nsloc = {} # locals to execute code with
nsglob = {} # globals to execute code with
# We could use one namespace for both but that doesn't matter
exec x.to_code() in nsglob, nsloc
print nsloc
И, результат, как и ожидалось:
{'a': 3}
Надеюсь, это помогло.