前言:
如果你跟我一样,对python的字节码感兴趣,想了解python的代码在内存中到底是怎么去运行的,那么你可以继续往下看,如果你是python新手,我建议你移步它处,本文适合有点基础的python读者。
如果你不知道怎么生成python的字节码文件,可以查阅我的 python 代码反汇编 的博文
python代码的执行过程:
- python代码编译成字节码【类似于汇编指令的中间语言】
- 字节码由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
案例五
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
STORE_MAP
案例七
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
BINARY_SUBSCR
STORE_SUBSCR
案例八
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
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