目 录CONTENT

文章目录

【Python】查看Python代码字节码

EulerBlind
2025-07-01 / 0 评论 / 0 点赞 / 0 阅读 / 0 字

背景

在日常工作中,经常会涉及到代码优化,直观来看,我们可以通过代码的运行速度、代码易读性等方面来判断我们代码优化是否有效,但对于一些比较苛刻的场景,就需要根据字节码来排查。这里介绍如何查看python字节码以及如何分析字节码的方法。

示例1

方法构造

def func(x, y):
    z = x + y
    return z

点击并拖拽以移动

查看字节码

import dis
dis.dis(func)

点击并拖拽以移动

得到如下结果

  1           0 RESUME                   0
​
  2           2 LOAD_FAST                0 (x)
              4 LOAD_FAST                1 (y)
              6 BINARY_OP                0 (+)
             10 STORE_FAST               2 (z)
​
  3          12 LOAD_FAST                2 (z)
             14 RETURN_VALUE

点击并拖拽以移动

字节码解读

  1. **第一行 **0 RESUME 0 是一个标签,标识了字节码的起始位置。
  2. **第二行是加载 **x 变量的值到栈顶,使用 LOAD_FAST 指令。接着,加载 y 变量的值到栈顶,也使用 LOAD_FAST 指令。然后,使用 BINARY_OP 指令进行加法操作,将 xy 的值相加,并将结果压入栈顶。最后,使用 STORE_FAST 指令将结果存储到变量 z 中。
  3. **第三行是加载变量 **z 的值到栈顶,使用 LOAD_FAST 指令。
  4. **最后一行使用 **RETURN_VALUE 指令将栈顶的值作为函数的返回值返回。

示例2

方法代码

该方法展示一个简单的递归求阶乘的方法

def factorial(n):
    if n == 0:
        return 1
    else:
        return n * factorial(n - 1)

点击并拖拽以移动

字节码查看

dis.dis(factorial) 得到如下结果

  1           0 RESUME                   0
​
  2           2 LOAD_FAST                0 (n)
              4 LOAD_CONST               1 (0)
              6 COMPARE_OP               2 (==)
             12 POP_JUMP_FORWARD_IF_FALSE     2 (to 18)
​
  3          14 LOAD_CONST               2 (1)
             16 RETURN_VALUE
​
  5     >>   18 LOAD_FAST                0 (n)
             20 LOAD_GLOBAL              1 (NULL + factorial)
             32 LOAD_FAST                0 (n)
             34 LOAD_CONST               2 (1)
             36 BINARY_OP               10 (-)
             40 PRECALL                  1
             44 CALL                     1
             54 BINARY_OP                5 (*)
             58 RETURN_VALUE

点击并拖拽以移动

字节码解读

  1. **第一行 **0 RESUME 0 是一个标签,标识了字节码的起始位置。
  2. **第二行是加载局部变量 **n 到栈顶,使用 LOAD_FAST 指令。然后加载常量 0 到栈顶,使用 LOAD_CONST 指令。接着使用 COMPARE_OP 指令比较栈顶的两个值是否相等,如果不相等,则跳转到标签 to 18;如果相等,则执行下一条指令。
  3. **接着是加载常量 **1 到栈顶,使用 LOAD_CONST 指令,然后使用 RETURN_VALUE 指令将栈顶的值作为函数的返回值返回。
  4. **标签 **to 18 表示下面的指令块是一个循环体,它用来计算阶乘。
  5. **第五行是加载局部变量 **n 到栈顶,使用 LOAD_FAST 指令。然后加载全局变量 NULL + factorial 到栈顶,使用 LOAD_GLOBAL 指令。接着加载局部变量 n 到栈顶,再加载常量 1 到栈顶,使用 BINARY_OP 指令计算 n - 1。然后使用 PRECALL 指令指示接下来的 CALL 指令调用栈顶的函数。接着使用 BINARY_OP 指令将函数返回值与 n 相乘,最后使用 RETURN_VALUE 指令将栈顶的值作为函数的返回值返回。

常见字节码指令

指令 描述
LOAD_CONST 加载常量到栈顶
LOAD_FAST 加载局部变量到栈顶
LOAD_GLOBAL 加载全局变量到栈顶
LOAD_ATTR 加载对象属性到栈顶
STORE_FAST 存储值到局部变量
STORE_GLOBAL 存储值到全局变量
STORE_ATTR 存储值到对象属性
BINARY_ADD 执行二元加法操作
BINARY_SUBTRACT 执行二元减法操作
BINARY_MULTIPLY 执行二元乘法操作
UNARY_NEGATIVE 执行一元取负操作
UNARY_NOT 执行一元逻辑取反操作
COMPARE_OP 比较操作
POP_TOP 弹出栈顶元素
ROT_TWO 交换栈顶的两个元素的位置
ROT_THREE 旋转栈顶的三个元素的位置
ROT_FOUR 旋转栈顶的四个元素的位置
CALL_FUNCTION 调用函数
RETURN_VALUE 返回函数值
JUMP_FORWARD 向前跳转
JUMP_ABSOLUTE 绝对跳转
JUMP_IF_FALSE_OR_POP 如果条件为假则跳转或弹出栈顶元素
0

评论区