Python 字节码中的所有零是什么以及如何计算它们

问题描述 投票:0回答:1

当我这样做时

list(some_function.__code__.co_code)
我可以看到该函数的实际字节码(以 list[int] 格式)。我发现有很多零——实际上比早期版本的 python 还要多。是的,我见过 this 问题,但是如果我在 python 3.12 中创建该函数,字节码中会有更多零。我的问题是:“所有这些零意味着什么?如果我想编写字节码,我如何计算需要多少个零?”

看看会发生什么:

def f(x):
    return x + x/3

bytecode = list(f.__code__.co_code)
print(bytecode)

打印:

[151, 0, 124, 0, 100, 1, 124, 0, 122, 11, 0, 0, 122, 0, 0, 0, 83, 0]

def f(x):
    return x + x/3

dis.dis(f, show_caches=True)

给出:

  1           0 RESUME                   0
  2           2 LOAD_FAST                0 (x)
              4 LOAD_FAST                0 (x)
              6 LOAD_CONST               1 (3)
              8 BINARY_OP               11 (/)
             10 CACHE                    0 (counter: 0)
             12 BINARY_OP                0 (+)
             14 CACHE                    0 (counter: 0)
             16 RETURN_VALUE

这与已经提到的代码有几点不同:

  • 首先我们前面有
    RESUME
    ,这是为了调试目的,如官方dis参考中提到的
  • 完成二进制操作时也略有不同,不是使用一个字节码代表一个操作,而是只有一个操作码,用于所有操作,并带有要执行哪个操作的参数。
  • 然而,主要区别在于,在某些点上零较少,而在其他点上较多......

这是怎么回事?为什么使用所有操作码一个参数(也是操作码小于

dis.HAVE_ARGUMENT
的那个)

这并不是“超级”奇怪,但在处理以下函数时却变得有些奇怪: def f(): print("hello world!")

字节码:
[151, 0, 116, 1, 0, 0, 0, 0, 0, 0, 0, 0, 100, 1, 171, 1, 0, 0, 0, 0, 0, 0, 1, 0, 121, 0]

有人也可以解释一下所有这些零吗?

提前致谢!

编辑

我看到所有的零都是 CACHE 操作码,但是如何计算需要多少个 CACHE?

编辑

有人建议字节码中的

0

是参数而不是 CACHE,但该断言似乎不正确。

查看带注释的输出:

def f(): print("hello world!") print(list(f.__code__.co_code)) for instr in dis.Bytecode(f): print(instr.opname, instr.opcode, instr.arg)

人们可以看到:

[151, 0, 116, 1, 0, 0, 0, 0, 0, 0, 0, 0, 100, 1, 171, 1, 0, 0, 0, 0, 0, 0, 1, 0, 121, 0] | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |--| |--|---------| | | | | | | | | |--|---------------| | | | | | | | | | | | | | | | | | | | RESUME 151 0 --| | | | | | | | | | LOAD_GLOBAL 116 1 ----| | | | | | | | | LOAD_CONST 100 1 -----------------------|--| | | | | | | CALL 171 1 -------------------------------|--| | | | | POP_TOP 1 None -------------------------------------------------------|--| | | RETURN_CONST 121 0 ---------------------------------------------------------------|--|

循环未指向的许多 
0

值被

dis.dis(f, show_caches=True)
 指示为“CACHE”
    

python bytecode python-3.12
1个回答
0
投票

有关编写字节码并将其转换为字节码文件的信息,请参阅

xasm

。这是在 PyPI 上的,但我已经有一段时间没有发布新版本了。因此从 github 上的源代码构建。 要了解字节码如何交互工作,请参阅

x-python

。关于 PyPI 和新版本的情况也是如此。对于 x-python,甚至还有一个调试器可以让您执行单步指令。这就是所谓的 trepan-xpy。这可能是最难安装的,因为有很多依赖项,例如 trepan 调试器 我将于 2024 年 4 月中旬在

BlackHat Asia

进行演讲,因此可能会在此之前的某个时间发布新版本。 在我尝试回答你的问题之前,让我先举一个类似的情况。假设我正在尝试学习数字。我看到有所有这些不同风格的基本系统(类似于 Python 字节码版本)。有人问为什么二进制版本的数字比 10 进制版本的数字有更多的零?嗯,这是因为可供选择的数字较少,因此每个数字出现的频率更高。

在字节码中,您会看到很多零,因为零是最小的整数,并且指令的许多操作数是某种表的索引,例如常量元组、变量名称元组、 tab 。如果元组不为空,则它将有 0 个项目。由于元组出于某些需要,第 0 项可能会有一条操作数为 0 的指令。

正如之前提到的 0,在字节码中也用作没有值时的占位符。因此,没有操作数的指令通常会在其中放入 0。我不知道这是否是严格要求的。有人可能会检查其他值是否有效,Python 解释器会忽略它,就像它忽略值为 0 的操作数一样。

© www.soinside.com 2019 - 2024. All rights reserved.