作者:蜗牛的家 | 来源:互联网 | 2022-11-06 14:47
给定一个由以下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
属性进行切片,该属性包含索引中的原始字节代码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
as 的值:
True
但是,如果我尝试仅提取并运行第二行的字节码,则该行的b = 'good stuff'
范围从索引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
exec(code2)
File "", line 1, in
SystemError: unknown opcode
调用dis.dis(code2)
将显示新的代码对象似乎包含正确的字节代码,用于b = 'good stuff'
:
1 0 LOAD_CONST 2 ('good stuff')
2 STORE_NAME 1 (b)
那我想念什么呢?
1> blhsing..:
我正在回答自己的问题,因为我找不到有关该主题的文档,并且花了我一段时间才能弄清我所缺少的内容,因此它可能会使碰巧遇到相同问题的其他人受益。
事实证明,每个代码块都需要返回值-不选择不返回值是不可行的。如果没有显式return
语句,则将None
隐式返回,如问题中显示的最后两个字节代码所示:
16 LOAD_CONST 4 (None)
18 RETURN_VALUE
因此,通过从索引的字节代码中切出字节码12
作为的最后一行c = True
,我无意中包含了尾随的隐式返回None
,幸运地满足了代码块返回值的要求。
当我尝试将字节码从index切8
到12
的第二行时,情况并非如此b = 'good stuff'
,因为它省略了最后两个字节码返回None
,从而导致SystemError: unknown opcode
异常。
因此,要解决此问题,所需要做的就是将最后两个字节代码(实际上总共4个字节,因为字节代码实际上已成为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