上次写到,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 玩耍 (下)的更多相关文章

  1. 理解 Python 的执行方式,与字节码 bytecode 玩耍 (上)

    这里有个博客讲 Python 内部机制,已经有一些中文翻译. 可能因为我用的Python 3.5,例子跑起来有些不一样. 此外,我又查了其他一些参考资料,总结如下: Python 的执行方式 先看一个 ...

  2. 任何Python线程执行前,必须先获得GIL锁,然后,每执行100条字节码,解释器就自动释放GIL锁,让别的线程有机会执行

    任何Python线程执行前,必须先获得GIL锁,然后,每执行100条字节码,解释器就自动释放GIL锁,让别的线程有机会执行 多线程 - 廖雪峰的官方网站 https://www.liaoxuefeng ...

  3. 从底层理解Python的执行

    摘要:是否想在Python解释器的内部晃悠一圈?是不是想实现一个Python代码执行的追踪器?没有基础?不要怕,这篇文章让你初窥Python底层的奥妙. [编者按]下面博文将带你创建一个字节码级别的追 ...

  4. Python 字节码bytecode

    字节码bytecode python把源码文件编译成字节码文件,存放在__pycahe子目录内,用.pyc结尾.之后如果不再修改源码文件,运行时则使用*.pyc文件编译成机器码,这样不但运行速度快,而 ...

  5. 深入理解java虚拟机(5)---字节码执行引擎

    字节码是什么东西? 以下是百度的解释: 字节码(Byte-code)是一种包含执行程序.由一序列 op 代码/数据对组成的二进制文件.字节码是一种中间码,它比机器码更抽象. 它经常被看作是包含一个执行 ...

  6. 深入理解JVM虚拟机5:虚拟机字节码执行引擎

    虚拟机字节码执行引擎   转自https://juejin.im/post/5abc97ff518825556a727e66 所谓的「虚拟机字节码执行引擎」其实就是 JVM 根据 Class 文件中给 ...

  7. 深入理解Java虚拟机06--虚拟机字节码执行引擎

    一.前言 物理机的执行引擎是直接在物理硬件如CPU.操作系统.指令集上运行的,但是对于虚拟机来讲,他的执行引擎由自己实现. 执行引擎有统一的外观(Java虚拟机规范),不同类型的虚拟机都遵循了这一规范 ...

  8. JVM学习第三天(JVM的执行子系统)之字节码指令

    早上看了Class类文件结构,晚上继续来看字节码指令,毕竟谁也不是一步登天的(说白了还是穷); 字节码指令 Java虚拟机的指令由一个字节长度的.代表着某种特定操作含义的数字(称为操作码,Opcode ...

  9. python常用执行方式&变量&input函数

    linux系统中执行py文件方式:  ./a.py 需要执行权限 chmod -R 777(最大权限) 常用执行方式: 1. ./a.py2. python a.py 文件内部头加上 #!/usr/b ...

随机推荐

  1. js中遇到的一些方法和函数

    这是一个笔记文章,方便日后复习. 加号的优先级高于三目运算符: console.log(') ? 'define' : 'undefine');//define setTimeout(code,mil ...

  2. php生成红包

    <?php /** * 随机生成红包金额 * @param $n 红包个数 * @param $sum 总金额 整数 * @param $index_max 最大金额在数组中索引 * @para ...

  3. 650. 2 Keys Keyboard复制粘贴的次数

    [抄题]: Initially on a notepad only one character 'A' is present. You can perform two operations on th ...

  4. Linux驱动之中断处理体系结构简析

    S3C2440中的中断处理最终是通过IRQ实现的,在Linux驱动之异常处理体系结构简析已经介绍了IRQ异常的处理过程,最终分析到了一个C函数asm_do_IRQ,接下来继续分析asm_do_IRQ, ...

  5. React-router4 第九篇 Ambiguous Matches 模糊匹配

    https://reacttraining.com/react-router/web/example/ambiguous-matches 看了官方的例子,我准备把阮一峰老师的代码再粘贴一次..!!

  6. javaweb开发3.基于Servlet+JSP+JavaBean开发模式的用户登录注册

    转载孤傲苍狼博客http://www.cnblogs.com/xdp-gacl/p/3902537.html 1.层次比较分明的项目结构图

  7. AJAX html 传输json字符串&&巧妙运用eval()来解析返回的JSON字符串

    1.AJAX html 传输json字符串: js方法如下: function saveRetYwlsh(){ var xmbh = document.getElementById("xmb ...

  8. 主机性能监控之wmi 获取磁盘信息

    标 题: 主机性能监控之wmi 获取磁盘信息作 者: itdef链 接: http://www.cnblogs.com/itdef/p/3990541.html 欢迎转帖 请保持文本完整并注明出处 仅 ...

  9. 75.iOS内存管理

    堆区和栈区 1.栈区:由编译器自动分配释放,函数的参数值,局部变量等值 2.堆区:一般由开发人员分配释放,若不释放,则可能会引起内存泄漏 NSString *string = @"abcd& ...

  10. Linux下 刚安装完mysql 修改密码

    在Centos中安装MySQL后默认的是没有root密码的,默认的是回车, 那么为了方便需要修改密码. 没有密码为MYSQL加密码: mysql -uroot -p 回车 提示输入密码,为空回车 up ...