【Python】查看Python代码字节码

背景

在日常工作中,经常会涉及到代码优化,直观来看,我们可以通过代码的运行速度、代码易读性等方面来判断我们代码优化是否有效,但对于一些比较苛刻的场景,就需要根据字节码来排查。这里介绍如何查看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如果条件为假则跳转或弹出栈顶元素

  • 20
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值