python 字节码死磕

时间:2022-12-13 17:04:48

python 字节码死磕
前言:

   如果你跟我一样,对python的字节码感兴趣,想了解python的代码在内存中到底是怎么去运行的,那么你可以继续往下看,如果你是python新手,我建议你移步它处,本文适合有点基础的python读者。
   如果你不知道怎么生成python的字节码文件,可以查阅我的 python 代码反汇编  的博文  
 
 
python代码的执行过程:
  1. python代码编译成字节码【类似于汇编指令的中间语言】
  2. 字节码由python虚拟机来执行编译后的字节码
 
说明:
          一个python语句会对 应若个字节码指令,每个字节指令又对应着一个函数偏移量,可以理解为指令的ID
 
        虚拟机一条一条执行字节码指令,从而完成程序的执行,而dis模块可以对CPython代码进行反汇编,生成字节码指令
 
 
dis.dis() 转化后的字节码格式如下:
源码行号  |  指令偏移量  | 指令符号 | 指令参数  |  实际参数值
       
说明: 不同版本的CPython 指令长度可能不同,但是 3.7的每条指令是2个字节,所以我们去看dis 生成的字节码指令集的时候,指令偏移量总是从0开始,每增加一条在原来的偏移量上增加2
    故,指令偏移量的值,一般都是: 0 , 2 , 4, 6 , 8 , ... , 2n ( n>=0 )
 
变量指令解析

 
变量 — _const
 
LOAD_CONST :加载 const 变量,比如数值,字符串等等, 一般用于传递给函数作为参数
 
案例一:
test(2,'hello')

  

对应的字节码指令
 
1             0 LOAD_NAME                0 (test)    
              2 LOAD_CONST               0 (2)
              4 LOAD_CONST               1 ('hello')
              6 CALL_FUNCTION            2            
              8 POP_TOP
             10 LOAD_CONST               2 (None)
             12 RETURN_VALUE

 

 
 
局部变量 — _FAST
 
LOAD_FAST :一般用于加载局部变量的值,也就是读取值,用于计算或者函数调用传传等
STORE_FAST :一般用于保存值到局部变量
 
案例二:
 
n = n / p
 
对应的字节码指令
1             0 LOAD_NAME                0 (n)
              2 LOAD_NAME                1 (p)
              4 BINARY_TRUE_DIVIDE
              6 STORE_NAME               0 (n)
              8 LOAD_CONST               0 (None)
             10 RETURN_VALUE

 

 
说明: 函数的形参也是局部变量,那么如何区*部变量中的形参呢?
    形参是没有初始化的,所以如果发现发现操作的一个局部变量只有 LOAD_FAST 而没有 STORE_FAST,那么这个变量就是形参了。而其它的局部变量在使用之前肯定会使用STORE_FAST进行初始化。
 
案例三:
def test(arg1):
     num = 0
     print(num, arg1)
 
对应的字节码指令
  1           0 LOAD_CONST               0 (<code object test at 0x10546c150, file "code.py", line 1>)
              2 LOAD_CONST               1 ('test')
              4 MAKE_FUNCTION            0
              6 STORE_NAME               0 (test)
              8 LOAD_CONST               2 (None)
             10 RETURN_VALUE
 
Disassembly of <code object test at 0x10546c150, file "code.py", line 1>:
  2           0 LOAD_CONST               1 (0)
              2 STORE_FAST               1 (num)
 
  3           4 LOAD_GLOBAL              0 (print)
              6 LOAD_FAST                1 (num)
              8 LOAD_FAST                0 (arg1).  #只有LOAD_FAST ,没有 STORE_FAST
             10 CALL_FUNCTION            2
             12 POP_TOP
             14 LOAD_CONST               0 (None)
             16 RETURN_VALUE

 

全局变量 — _GLOBAL
 
    LOAD_GLOBAL : 用来加载全局变量, 包括制定函数名,类名,模块名等全局符号
    STORE_GLOBAL :用来给全局变量赋值
 
案例四
def test(arg1):
     global age
     age = 20
     print(age)

  

对应的字节码指令
  1           0 LOAD_CONST               0 (<code object test at 0x1056e3150, file "code.py", line 1>)
              2 LOAD_CONST               1 ('test')
              4 MAKE_FUNCTION            0
              6 STORE_NAME               0 (test)
              8 LOAD_CONST               2 (None)
             10 RETURN_VALUE
 
Disassembly of <code object test at 0x1056e3150, file "code.py", line 1>:
  3           0 LOAD_CONST               1 (20)
              2 STORE_GLOBAL             0 (age)
 
  4           4 LOAD_GLOBAL              1 (print)
              6 LOAD_GLOBAL              0 (age)
              8 CALL_FUNCTION            1
             10 POP_TOP
             12 LOAD_CONST               0 (None)
             14 RETURN_VALUE
 
常用数据类型

 
1.list
 
BUILD_LIST :  用于创建一个 list 结构
 
案例五
a = [1, 2]
 
对应的字节码指令
  1           0 LOAD_CONST               0 (1)
              2 LOAD_CONST               1 (2)
              4 BUILD_LIST               2
              6 STORE_NAME               0 (a)
              8 LOAD_CONST               2 (None)
             10 RETURN_VALUE                      //程序结束
 
案例六
[ x for x in range(4) if x > 2 ]
 
对应的字节码
  1           0 LOAD_CONST               0 (<code object <listcomp> at 0x10bffa150, file "code.py", line 1>)
              2 LOAD_CONST               1 ('<listcomp>')
              4 MAKE_FUNCTION            0
              6 LOAD_NAME                0 (range)
              8 LOAD_CONST               2 (4)
             10 CALL_FUNCTION            1
             12 GET_ITER
             14 CALL_FUNCTION            1
             16 POP_TOP
             18 LOAD_CONST               3 (None)
             20 RETURN_VALUE
 
Disassembly of <code object <listcomp> at 0x10bffa150, file "code.py", line 1>:
  1           0 BUILD_LIST               0               //创建 list , 为赋值给某变量,这种时候一般都是语法糖结构了
              2 LOAD_FAST                0 (.0)        
        >>    4 FOR_ITER                16 (to 22)      //开启迭代循环
              6 STORE_FAST               1 (x)          //局部变量x
              8 LOAD_FAST                1 (x)          // 导入 x
             10 LOAD_CONST               0 (2)          // 导入 2
             12 COMPARE_OP               4 (>)          // x 与 2 进行比较,比较符号为 >
             14 POP_JUMP_IF_FALSE        4              // 不满足条件就跳过 “出栈“ 动作,既,continue" >>  4 FOR_ITER. “ 处
             16 LOAD_FAST                1 (x)          // 读取满足条件的局部变量x
             18 LIST_APPEND              2              // 把满足条件的x 添加到list中
             20 JUMP_ABSOLUTE            4              
        >>   22 RETURN_VALUE                            //程序结束
 
 
2.dict
 
BUILD_MAP : 用于创建一个空的dict 
STORE_MAP : 用于初始化 dict 中的内容,赋值给变量
 
案例七
k = {'a': 1}
 
对应的字节码
  1           0 LOAD_CONST               0 ('a')
              2 LOAD_CONST               1 (1)
              4 BUILD_MAP                1
              6 STORE_NAME               0 (k)
              8 LOAD_CONST               2 (None)
             10 RETURN_VALUE
 
 
3.slice
 
BUILD_SLICE :        用于创建切片, 对于 list , tuple , 字符串都可以使用slice 的方式进行访问
BINARY_SUBSCR : 读取slice 的值
STORE_SUBSCR :   slice 的值赋值给变量。
 
案例八
num = [1, 2, 3]
a = num[1:2]
b = num[0:1:1]
num[1:2] = [10, 11]

  

对应的字节码
  1           0 LOAD_CONST               0 (1)
              2 LOAD_CONST               1 (2)
              4 LOAD_CONST               2 (3)
              6 BUILD_LIST               3
              8 STORE_NAME               0 (num)
 
  2          10 LOAD_NAME                0 (num)
             12 LOAD_CONST               0 (1)
             14 LOAD_CONST               1 (2)
             16 BUILD_SLICE              2       #创建了一个切片
             18 BINARY_SUBSCR                 #读取切片中的值
             20 STORE_NAME               1 (a)   #将读取切片中的值赋值给变量 a
 
  3          22 LOAD_NAME                0 (num)
             24 LOAD_CONST               3 (0)
             26 LOAD_CONST               0 (1)
             28 LOAD_CONST               0 (1)
             30 BUILD_SLICE              3
             32 BINARY_SUBSCR
             34 STORE_NAME               2 (b)
 
  4          36 LOAD_CONST               4 (10)
             38 LOAD_CONST               5 (11)
             40 BUILD_LIST               2
             42 LOAD_NAME                0 (num)
             44 LOAD_CONST               0 (1)
             46 LOAD_CONST               1 (2)
             48 BUILD_SLICE              2
             50 STORE_SUBSCR
             52 LOAD_CONST               6 (None)
             54 RETURN_VALUE
 
 
4.循环
 
SETUP_LOOP :用于开始一个循环。 
JUMP_ABSOLUTE: 结束循环
 
案例九
i = 0
while i < 10:
    i += 1

  

对应的字节码
  1           0 LOAD_CONST               0 (0)
              2 STORE_NAME               0 (i)
 
  2           4 SETUP_LOOP              20 (to 26)   // 循环开始处,26表示循环结束点
        >>    6 LOAD_NAME                0 (i)       // “>>" 表示循环切入点
              8 LOAD_CONST               1 (10)
             10 COMPARE_OP               0 (<)
             12 POP_JUMP_IF_FALSE       24
 
  3          14 LOAD_NAME                0 (i)
             16 LOAD_CONST               2 (1)
             18 INPLACE_ADD
             20 STORE_NAME               0 (i)
             22 JUMP_ABSOLUTE            6          // 逻辑上,循环在此处结束
        >>   24 POP_BLOCK                          
        >>   26 LOAD_CONST               3 (None)
             28 RETURN_VALUE 
 
案例十
num = 0
for i in range(5):
    num += i

 

对应的字节码
  1           0 LOAD_CONST               0 (0)
              2 STORE_NAME               0 (num)
 
  2           4 SETUP_LOOP              24 (to 30)  //开始循环
              6 LOAD_NAME                1 (range)
              8 LOAD_CONST               1 (5)
             10 CALL_FUNCTION            1          //调用range 函数
             12 GET_ITER                            //获取迭代 range 的 iter 
        >>   14 FOR_ITER                12 (to 28)  //开始进行 range 的迭代
             16 STORE_NAME               2 (i)
 
  3          18 LOAD_NAME                0 (num)
             20 LOAD_NAME                2 (i)
             22 INPLACE_ADD
             24 STORE_NAME               0 (num)
             26 JUMP_ABSOLUTE           14
        >>   28 POP_BLOCK
        >>   30 LOAD_CONST               2 (None)
             32 RETURN_VALUE
 
 
5.if
 
POP_JUMP_IF_FALSE : 条件结果为 FALSE  则跳出 目标的偏移指令
JUMP_FORWARD :       直接跳转到目标便宜指令
COMPARE_OP:             比较指令
 
案例十一
num = 20
if num < 10:
    print('lt 10')
elif num > 10:
    print('gt 10')
else:
    print('eq 10')

对应的字节码
  1           0 LOAD_CONST               0 (20)
              2 STORE_NAME               0 (num)
 
  2           4 LOAD_NAME                0 (num)
              6 LOAD_CONST               1 (10)
              8 COMPARE_OP               0 (<)
             10 POP_JUMP_IF_FALSE       22
 
  3          12 LOAD_NAME                1 (print)
             14 LOAD_CONST               2 ('lt 10')
             16 CALL_FUNCTION            1
             18 POP_TOP
             20 JUMP_FORWARD            26 (to 48)
 
  4     >>   22 LOAD_NAME                0 (num)
             24 LOAD_CONST               1 (10)
             26 COMPARE_OP               4 (>)
             28 POP_JUMP_IF_FALSE       40
 
  5          30 LOAD_NAME                1 (print)
             32 LOAD_CONST               3 ('gt 10')
             34 CALL_FUNCTION            1
             36 POP_TOP
             38 JUMP_FORWARD             8 (to 48)
 
  7     >>   40 LOAD_NAME                1 (print)
             42 LOAD_CONST               4 ('eq 10')
             44 CALL_FUNCTION            1
             46 POP_TOP
        >>   48 LOAD_CONST               5 (None)
             50 RETURN_VALUE 

 

参考资料:

  python 官方 dis介绍