理解 Python 的执行方式,与字节码 bytecode 玩耍 (下)
上次写到,Python 的执行方式是把代码编译成bytecode(字节码)指令,然后由虚拟机来执行这些 bytecode
而 bytecode 长成这个样子: b'|\x00\x00d\x01\x00\x14S' 。显然这个样子适合机器看,不适合人类看。
虽然你可以通过查字典的方式,手动把这段 bytecode 编写成人类可以看得懂的样子,
但是这么劳累的事情,为什么要自己亲手来做呢,让你的男仆机器来做不就好了吗。
Python 的反汇编工具 dis 就可以办到这件事。下面用绚丽的紫色来对dis.dis 的输出结果进行分列解释。
>>> def double(a):
return a*2 # 并不知道为什么贴在这里缩进会是这样 >>> import dis
>>> dis.dis(double)
2 0 LOAD_FAST 0 (a)
3 LOAD_CONST 1 (2)
6 BINARY_MULTIPLY
7 RETURN_VALUE
在上一篇文章的末尾已经解释过了,第2列的数字 0 3 6 7 是 bytecode 的偏移量。
第3列很好理解,都是opcode。注意这些 opcode 是给弱弱的人类看的,不是给机器看的,机器只要看b'|\x00\x00d\x01\x00\x14S' 这种东西就行了),
比如第一个opcode, 大名叫做叫LOAD_FAST,查一下资料,发现它的意思是 Pushes a reference to the local co_varnames[var_num] onto the stack.
中文意思:把本地某个东西的引用压到栈里。
什么东西呢?那就是 co_varnames[var_num] 啦。
(内心OS: 看这形象似乎是列表或者字典,字符串不太可能,自定义对象更不可能……)
>>> double.__code__.co_varnames[0] # 第4列里的下标 0
'a'
>>> double.__code__.co_consts[1] # 第4列里的下标 1
2
上次讲过了:double 是函数对象 function object
double.__code__是这个函数对象的代码对象 code object
看看返回值,a 和2, 也就是第5列
那么第1列的2是什么?看起来好像很神秘的样子,其实不过是源代码中的行号。本例中表示是在double 代码的第2行。
上次详细解释过,b'|\x00\x00d\x01\x00\x14S' 其实是8个整数
>>> double.__code__.co_code
b'|\x00\x00d\x01\x00\x14S'
>>> for i in double.__code__.co_code:
print (i, end=" ") 124 0 0 100 1 0 20 83
通过查字典或者另一个更加巧妙正常的办法,你可以找出124代表的opcode是 LOAD_FAST, 100代表 LOAD_CONST
这类opcode 后面各带两个字节的参数,分别是0 0 和 1 0
但有些opcode后面是没有参数的,比如 83 代表的 RETURN_VALUE
不了解的人可能会觉得有点奇怪,为什么RETURN_VALUE不带参数呢,不带参数怎么返回结果呢?
查一查资料,RETURN_VALUE的意思是 Returns with TOS to the caller of the function. 即把TOS返回给这个函数的调用者。TOS= top of stack, 。咱出栈了。
现在问题来了:为什么要与字节码 bytecode 玩耍? 直接写Python代码方便多了,为什么要去写字节码?
因为可以节省编译时间,这里有一篇非常详细的文章,作者在遗传编程领域工作,发现他们Python 程序的总运算时间中,有50%都被编译过程吃掉。于是作者深入到 bytecode 层次进行了小小改动,大幅削减了编译时间,把总的运算时间降至不足原先的一半。
可惜没有找到这篇文章的中文翻译。不知道有没有人肯出钱让我翻译。
文中写道:
bytecode 写好之后,我们必须让Python 明白它要执行这些bytecode。这时就需要创建一个完整的 code object。
bytecode 是 code object 的主要成分,但是还需要其他东西才能构成完整的 code object。就像鸡丁是宫保鸡丁中的主要食材,但它也不能没有花生。
这个过程中要用到types.CodeType()
有了code object 之后,接下来可以调用Types.FunctionType,利用这些代码创建一个函数对象( function object)。
上一篇写了怎么抽丝剥茧,顺着function object 找 code object,再找 bytecode,这里就完全倒过来,添枝加叶,逆流而上了。
理解 Python 的执行方式,与字节码 bytecode 玩耍 (下)的更多相关文章
- 理解 Python 的执行方式,与字节码 bytecode 玩耍 (上)
这里有个博客讲 Python 内部机制,已经有一些中文翻译. 可能因为我用的Python 3.5,例子跑起来有些不一样. 此外,我又查了其他一些参考资料,总结如下: Python 的执行方式 先看一个 ...
- 任何Python线程执行前,必须先获得GIL锁,然后,每执行100条字节码,解释器就自动释放GIL锁,让别的线程有机会执行
任何Python线程执行前,必须先获得GIL锁,然后,每执行100条字节码,解释器就自动释放GIL锁,让别的线程有机会执行 多线程 - 廖雪峰的官方网站 https://www.liaoxuefeng ...
- 从底层理解Python的执行
摘要:是否想在Python解释器的内部晃悠一圈?是不是想实现一个Python代码执行的追踪器?没有基础?不要怕,这篇文章让你初窥Python底层的奥妙. [编者按]下面博文将带你创建一个字节码级别的追 ...
- Python 字节码bytecode
字节码bytecode python把源码文件编译成字节码文件,存放在__pycahe子目录内,用.pyc结尾.之后如果不再修改源码文件,运行时则使用*.pyc文件编译成机器码,这样不但运行速度快,而 ...
- 深入理解java虚拟机(5)---字节码执行引擎
字节码是什么东西? 以下是百度的解释: 字节码(Byte-code)是一种包含执行程序.由一序列 op 代码/数据对组成的二进制文件.字节码是一种中间码,它比机器码更抽象. 它经常被看作是包含一个执行 ...
- 深入理解JVM虚拟机5:虚拟机字节码执行引擎
虚拟机字节码执行引擎 转自https://juejin.im/post/5abc97ff518825556a727e66 所谓的「虚拟机字节码执行引擎」其实就是 JVM 根据 Class 文件中给 ...
- 深入理解Java虚拟机06--虚拟机字节码执行引擎
一.前言 物理机的执行引擎是直接在物理硬件如CPU.操作系统.指令集上运行的,但是对于虚拟机来讲,他的执行引擎由自己实现. 执行引擎有统一的外观(Java虚拟机规范),不同类型的虚拟机都遵循了这一规范 ...
- JVM学习第三天(JVM的执行子系统)之字节码指令
早上看了Class类文件结构,晚上继续来看字节码指令,毕竟谁也不是一步登天的(说白了还是穷); 字节码指令 Java虚拟机的指令由一个字节长度的.代表着某种特定操作含义的数字(称为操作码,Opcode ...
- python常用执行方式&变量&input函数
linux系统中执行py文件方式: ./a.py 需要执行权限 chmod -R 777(最大权限) 常用执行方式: 1. ./a.py2. python a.py 文件内部头加上 #!/usr/b ...
随机推荐
- Pandas合并数据集之merge、join方法
合并数据集 pandas.merge 可根据一个或多个键将不同DataFrame中的行连接起来. pandas.concat 可以沿着一条轴将多个对象堆叠到一起. combine_first merg ...
- 每月IT摘录201903
技术 1.在开发高并发系统时,有很多手段来保护系统,如缓存.降级.限流等.缓存可以提升系统的访问速度,降级可以暂时屏蔽掉非核心业务,使得核心业务不受影响.限流的目的是通过对并发访问进行限速,一旦达到一 ...
- 利用DWORD SHOOT实现堆溢出的利用(先知收录)
原文链接:https://xz.aliyun.com/t/4009 1.0 DWORD SHOOT是什么捏? DWORD SHOOT指能够向内存任意位置写入任意数据,1个WORD=4个bytes,即可 ...
- vue去掉地址栏#(带来的后果)
按以下修改后,带来的后果:打包后部分图片访问不到,首页没加载router-view内容 router 的index中配置模式设置为history export default new Router({ ...
- 529. Minesweeper扫雷游戏
[抄题]: Let's play the minesweeper game (Wikipedia, online game)! You are given a 2D char matrix repre ...
- pythone函数基础(8)内置函数学习
内置函数学习# sorted# map# filter# max# sum# round# chr# ord# dir# bool# eval# exec# zipimport mathres = m ...
- springboot与缓存(redis,或者caffeine,guava)
1.理论介绍 Java Caching定义了5个核心接口,分别是CachingProvider, CacheManager, Cache, Entry 和 Expiry. CachingProvide ...
- Yii2增删改查
Controller <?php namespace frontend\controllers; use frontend\models\User; use yii\data\Paginatio ...
- Android 记录点滴
1:关于断点 设置断点点三角是进不去的,这个是类似c#的release 正式版, 点第二个红圈内的debug的那个按钮才可以 . 这个按钮可以让程序及时进入当前断点处 2:对于背景颜色 andro ...
- surf the internet scientifically
You can get some useful information from the link below: 1. http://blog.sina.com.cn/s/blog_4891cbc50 ...