#python #python-3.x #bytecode
#python #python-3.x #байт-код
Вопрос:
Учитывая объект code, скомпилированный из следующих 3 строк кода:
code = compile('''a = 1 / 0 # bad stuff. avoid running this!
b = 'good stuff'
c = True''', '', 'exec')
какой вызов dis.dis(code)
разобрал бы на:
1 0 LOAD_CONST 0 (1)
2 LOAD_CONST 1 (0)
4 BINARY_TRUE_DIVIDE
6 STORE_NAME 0 (a)
2 8 LOAD_CONST 2 ('good stuff')
10 STORE_NAME 1 (b)
3 12 LOAD_CONST 3 (True)
14 STORE_NAME 2 (c)
16 LOAD_CONST 4 (None)
18 RETURN_VALUE
Как мне извлечь и запустить только байтовые коды для второй строки, b = 'good stuff'
?
Например, если я хочу извлечь и запустить только байтовые коды для последней строки, c = True
которая начинается с байтового индекса 12
, я могу вырезать co_code
атрибут объекта code, который содержит необработанные байтовые коды, из index 12
, чтобы создать types.CodeType
объект, а затем вызвать exec
с ним:
import types
code3 = types.CodeType(
code.co_argcount,
code.co_kwonlyargcount,
code.co_nlocals,
code.co_stacksize,
code.co_flags,
code.co_code[12:],
code.co_consts,
code.co_names,
code.co_varnames,
code.co_filename,
code.co_name,
code.co_firstlineno,
code.co_lnotab,
code.co_freevars,
code.co_cellvars)
exec(code3)
print(eval('c'))
так что он правильно выводит значение c
как назначенное:
True
Однако, если я попытаюсь извлечь и запустить только байтовые коды для второй строки, b = 'good stuff'
которая варьируется от index 8
до 12
(не включая 12
):
code2 = types.CodeType(
code.co_argcount,
code.co_kwonlyargcount,
code.co_nlocals,
code.co_stacksize,
code.co_flags,
code.co_code[8:12],
code.co_consts,
code.co_names,
code.co_varnames,
code.co_filename,
code.co_name,
code.co_firstlineno,
code.co_lnotab,
code.co_freevars,
code.co_cellvars)
exec(code2)
print(eval('b'))
это приводит:
XXX lineno: 1, opcode: 0
Traceback (most recent call last):
File "/path/file.py", line 21, in <module>
exec(code2)
File "", line 1, in <module>
SystemError: unknown opcode
Вызов dis.dis(code2)
покажет, что новый объект code, по-видимому, содержит правильные байтовые коды для b = 'good stuff'
:
1 0 LOAD_CONST 2 ('good stuff')
2 STORE_NAME 1 (b)
Итак, чего мне не хватает?
Ответ №1:
Я отвечаю на свой собственный вопрос, потому что я не смог найти документации по этой теме, и мне потребовалось некоторое время, чтобы выяснить, чего мне не хватает, чтобы это могло принести пользу другим, которые столкнулись с такой же проблемой.
Оказывается, что для возврата значения требуется каждый блок кода — не возвращать значение не вариант. Если нет явного return
оператора, то None
будет возвращено неявно, как видно из последних двух байтовых кодов, показанных в вопросе:
16 LOAD_CONST 4 (None)
18 RETURN_VALUE
Итак, вырезая байтовые коды из index 12
для последней строки c = True
, я непреднамеренно включил завершающий неявный возврат None
, к счастью, удовлетворяющий требованию, чтобы блок кода возвращал значение.
Такого не было, когда я пытался нарезать байтовые коды из index 8
в 12
для второй строки b = 'good stuff'
, поскольку он пропустил последние два байтовых кода для возврата None
, тем самым вызывая SystemError: unknown opcode
исключение.
Итак, чтобы исправить это, все, что было необходимо, это добавить последние два байтовых кода (фактически всего 4 байта, поскольку байтовые коды фактически стали кодами «word» в Python 3) к фрагменту:
code2 = types.CodeType(
code.co_argcount,
code.co_kwonlyargcount,
code.co_nlocals,
code.co_stacksize,
code.co_flags,
code.co_code[8:12] code.co_code[-4:],
code.co_consts,
code.co_names,
code.co_varnames,
code.co_filename,
code.co_name,
code.co_firstlineno,
code.co_lnotab,
code.co_freevars,
code.co_cellvars)
exec(code2)
print(eval('b'))
Затем это будет корректно выводить:
good stuff
Комментарии:
1. Со вчерашнего дня без ума от этого, спасибо!