热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

执行切片的Python字节码有时会导致“SystemError:未知操作码”

如何解决《执行切片的Python字节码有时会导致“SystemError:未知操作码”》经验,为你挑选了1个好方法。

给定一个由以下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'))

以便正确输出cas 的值:

True

但是,如果我尝试仅提取并运行第二行的字节码,则该行的b = 'good stuff'范围从索引812(不包括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切812的第二行时,情况并非如此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


推荐阅读
author-avatar
蜗牛的家
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有