前言:

   如果你跟我一样,对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介绍

python 字节码死磕的更多相关文章

  1. JAVA NIO 简介 (netty源码死磕1.1)

    [基础篇]netty 源码死磕1.1:  JAVA NIO简介 1. JAVA NIO简介 Java 中 New I/O类库 是由 Java 1.4 引进的异步 IO.由于之前老的I/O类库是阻塞I/ ...

  2. Java NIO Buffer(netty源码死磕1.2)

    [基础篇]netty源码死磕1.2:  NIO Buffer 1. Java NIO Buffer Buffer是一个抽象类,位于java.nio包中,主要用作缓冲区.Buffer缓冲区本质上是一块可 ...

  3. Python 字节码是什么

    了解 Python 字节码是什么,Python 如何使用它来执行你的代码,以及知道它是如何帮到你的. 如果你曾经编写过 Python,或者只是使用过 Python,你或许经常会看到 Python 源代 ...

  4. 浮生半日:探究Python字节码

    好吧!“人生苦短,请用Python”,作为python爱好者以及安全从业者,而且最近也碰到了一些这方面的问题,懂点python字节码还是很有必要的. Python是一门解释性语言,它的具体工作流程如下 ...

  5. Netty环境搭建 (源码死磕2)

    [正文]netty源码  死磕2: 环境搭建 本小节目录 1. Netty为什么火得屌炸天? 1.1. Netty是什么? 1.2. Netty火到什么程度呢? 1.3. Netty为什么这么火? 2 ...

  6. EventLoop(netty源码死磕4)

    精进篇:netty源码  死磕4-EventLoop的鬼斧神工 目录 1. EventLoop的鬼斧神工 2. 初识 EventLoop 3. Reactor模式回顾 3.1. Reactor模式的组 ...

  7. Pipeline模式(netty源码死磕6)

    精进篇:netty源码死磕6  巧夺天工--Pipeline模式揭秘 1. 巧夺天工--Pipeline模式揭秘 1.1. Pipeline模式简介 管道的发名者叫,Malcolm Douglas M ...

  8. ChannelHandler揭秘(Netty源码死磕5)

    精进篇:netty源码死磕5  揭开 ChannelHandler 的神秘面纱 目录 1. 前言 2. Handler在经典Reactor中的角色 3. Handler在Netty中的坐标位置 4. ...

  9. Python逆向(五)—— Python字节码解读

    一.前言 前些章节我们对python编译.反汇编的原理及相关模块已经做了解读.读者应该初步掌握了通过反汇编获取python程序可读字节码的能力.python逆向或者反汇编的目的就是在没有源码的基础上, ...

随机推荐

  1. iOS 原生库(AVFoundation)实现二维码扫描,封装的工具类,不依赖第三方库,可高度自定义扫描动画及界面(Swift 4.0)

    Create QRScanner.swift file // // QRScanner.swift // NativeQR // // Created by Harvey on 2017/10/24. ...

  2. C 2015年真题【保】

    1.编写一个完整的程序,使之能完成以下功能:从键盘中输入若干个整数,用链表储存这些输入的数,并要求存储的顺序与输入的顺序相反. 分析:链表建立[头插法] 代码: #include <stdio. ...

  3. 2020.3.23 模拟赛游记 & 题解

    这次的模拟赛,实在是水. 数据水,\(\texttt{std}\) 水,出题人水,做题人也水.??? 游记就说一句: 水. T1 metro 弱智题. 人均 \(100pts\). #pragma G ...

  4. C#获取设备话筒主峰值(实时音频输出分贝量)

    1.引用类库NAudio,Git地址 https://github.com/naudio/NAudio 2.添加如下代码和引用: public float GetVoicePeakValue() { ...

  5. JavaScript模块化-CommonJS、AMD、CMD、UMD、ES6

    前言:模块化开发需求 在JS早期,使用script标签引入JS,会造成以下问题: 加载的时候阻塞网页渲染,引入JS越多,阻塞时间越长. 容易污染全局变量. js文件存在依赖关系,加载必须有顺序.项目较 ...

  6. OpenCV-Python 轮廓特征 | 二十二

    目标 在本文中,我们将学习 如何找到轮廓的不同特征,例如面积,周长,质心,边界框等. 您将看到大量与轮廓有关的功能. 1. 特征矩 特征矩可以帮助您计算一些特征,例如物体的质心,物体的面积等.请查看特 ...

  7. Ceph学习笔记(2)- CRUSH数据分布算法

    前言: ​ 分布式存储系统需要让数据均匀的分布在集群中的物理设备上,同时在新设备加入,旧设备退出之后让数据重新达到平衡状态尤为重要.新设备加入后,数据要从不同的老设备中迁移过来.老设备退出后,数据迁移 ...

  8. Java 注解 So Easy!!!

    Java注解 Annotations, a form of metadata, provide data about a program that is not part of the program ...

  9. iOS UmbrellaFramework

    一.umbrella framework 将几个已经封装好的 framework 封装成一个,封装的这种 framework 就是 umbrella framework. Apple 的官方文档中明确 ...

  10. 模块 jieba结巴分词库 中文分词

    jieba结巴分词库 jieba(结巴)是一个强大的分词库,完美支持中文分词,本文对其基本用法做一个简要总结. 安装jieba pip install jieba 简单用法 结巴分词分为三种模式:精确 ...