字节码bytecode

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

字节码,其实就是一种中间代码。

前置知识

在看字节码之前,先要了解一下code object。它们在datamodel.html中有介绍

例子:

>>> import dis
>>> def hello():
... print('Hello World!')
...
>>> hello.__code__
<code object hello at 0x1066683a0, file "<stdin>", line 1>

__code__属性显示了编译后的函数体the compiled function body, 它是一个code object。

code object 代码对象

code模块中的code类产生code对象。

>>> type(hello.__code__)
<class 'code'> 

code object一个python的内部类型。即解释器内部使用的类型。也称为bytecode。

它是python把源码编译成字节码时,创建的代码对象。

code类有多个个data attributes(实例变量),其中:

  • co_consts  为一个包含字节码所使用的字面值的元组。如果code object代表一个函数,这个属性的第一项是函数的文档字符串。
  • co_varnames 为一个包含局部变量名称的元组。
  • 'co_names': <member 'co_names' of 'code' objects> 在函数体内引用的非本地name的tuple

详细介绍见:https://docs.python.org/3/reference/datamodel.html#object.__repr__

例子:

>>> hello.__code__.co_consts
(None, 'Hello World!') >>> hello.__code__.co_varnames
()
>>> hello.__code__.co_names
('print',)

dis模块

使用上面的hello()函数。用dis.dis()方法, 它返回字节码操作的格式化试图。

>>> dis.dis("hello()")
1 0 LOAD_NAME 0 (hello)
2 CALL_FUNCTION 0
4 RETURN_VALUE
>>>
>>> dis.dis(hello)
2 0 LOAD_GLOBAL 0 (print)
2 LOAD_CONST 1 ('Hello World!')
4 CALL_FUNCTION 1
6 POP_TOP
8 LOAD_CONST 0 (None)
10 RETURN_VALUE

解释:

dis 模块为 Python 字节码提供了一个 “反汇编”,它可以让你更容易地得到一个人类可读的版本,以及查找各种字节码指令。

这是因为字节码是--非人类可读格式的字节。通过dis模块,就可以理解字节码。

dis.dis(hello)会在函数hello被编译之前,返汇编它:

  • 例子中,做左边的数字2代表编译该字节码的源代码中的行号。
  • 左边列中的数字是字节码中指令的偏移量()/也称为索引,即指令xxx所在位置。⚠️见本文底部例子的解释。
  • 右边的数字是字节码指令的参数,数字后面实际的参数,⚠️见本文底部例子。

本例子:

  • LOAD_GLOBAL(namei)加载名称为 co_names[namei] 的全局对象推入栈顶。

    • 本例子:因为hello.__code__.co_names是('print', ),所以就是把print函数对象推入栈顶。
  • LOAD_CONST(consti) 将co_consts[consti]推入栈顶。
    • 本例子:将'Hello World!'这个字符串常量推入栈顶。
  • CALL_FUNCTION(argc)  Calls a callable object with positional arguments.
    • “计算栈”的顶端就包括所有的位置参数。从栈中弹出所有的参数和可调用对象,
    • 然后把参数传入对象并调用对象,最后把返回的值推入栈。
      • ⚠️具体涉及到frame的生命周期和call stack的知识(尚未理解)
  • POP_TOP  移除栈顶(Top-of-Stack)的项目
  • LOAD_CONST(consti) 本例子是将None推入栈。因为没有指定函数的返回值,所以默认返回None。
  • RETURN_VALUE  把栈顶的值返回到the caller of the function。

了解字节码的用处

  1. 了解字节码,可以帮助理解python的运行模型。通过python字节码,可以知道如何更好的写和优化源码。
  2. 理解字节码可以帮助你更好回答一些疑惑:比如,为什么 {} 比 dict() 快? 这是因为执行代码的过程有区别,通过dis.dis("{}")和dis.dis("dict()")就明白了。
  3. 理解字节码和Python如何运行它,就会理解面向栈的编程。有助开拓视野。

延伸:

  • Python 虚拟机内幕,它是 Obi Ike-Nwosu 写的一本免费在线电子书,它深入 Python 解析器,解释了 Python 如何工作的细节。
  • 一个用 Python 编写的 Python 解析器,它是由 Allison Kaptur 写的一个教程,它是用 Python 构建的 Python 字节码解析器,并且它实现了运行 Python 字节码的全部构件。
  • 最后,CPython 解析器是一个开源软件,你可以在 GitHub 上阅读它。它在文件 Python/ceval.c 中实现了字节码解析器。这是 Python 3.6.4 发行版中那个文件的链接;字节码指令是由第 1266 行开始的 switch 语句来处理的。

本文摘录:https://zhuanlan.zhihu.com/p/39259061

知识点补充

例子1

>>> dis.opname[0x65]
'LOAD_NAME'
>>> dis.opname[101]
'LOAD_NAME' 

解释:

dis.opname会获得一个list,索引101对应的是一个字符串"LOAD_NAME"。

>>> dis.opmap['LOAD_NAME']
101

dis.opmap得到一个dict, 映射操作名称到字节码mapping operation names to bytecodes, 索引'LOAD_NAME'的值是十进制数字101。

101是一个十进制的字节码,对应的是"LOAD_NAME"

例子2

>>> x = dis.Bytecode(hello)
>>> x
Bytecode(<function hello at 0x106594b80>)
>>> for instr in x:
... print(instr.opname)
...
LOAD_GLOBAL
LOAD_CONST
CALL_FUNCTION
POP_TOP
LOAD_CONST
RETURN_VALUE

解释:

把函数hello分析为一个Bytecode对象。然后打印它内部的所有字节码的对应的人类可读的operation name。

其实就是dis.dis(hello)中的第3列。

>>> for instr in x:
... print(instr.opcode)
...
116
100
131
1
100
83

使用opcode打印出对应的字节码。116(这是十进制)对应LOAD_GLOBAL。

>>> hello.__code__
<code object hello at 0x1066683a0, file "<stdin>", line 1>
>>> hello.__code__.co_code
b't\x00d\x01\x83\x01\x01\x00d\x00S\x00'
>>> list(hello.__code__.co_code)
[116, 0, 100, 1, 131, 1, 1, 0, 100, 0, 83, 0]
>>> dis.opname[116]
'LOAD_GLOBAL'
>>> dis.dis(hello)
2 0 LOAD_GLOBAL 0 (print)
2 LOAD_CONST 1 ('Hello World!')
4 CALL_FUNCTION 1
6 POP_TOP
8 LOAD_CONST 0 (None)
10 RETURN_VALUE
>>> dis.opname[1]
'POP_TOP'

由此可知dis.dis的第2列数字其实是把字节码list化后的索引。最右边那一列的数字其实就是字节码的参数。

小结:

dis.opname:得到字节码的含义的list表。

dis.map: 一个dict,key是字节码的含义,value是十进制的字节码。

使用xx.__code__得到一个Code Object,用属性co_code可以得到字节码。

Python 字节码bytecode的更多相关文章

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

    上次写到,Python 的执行方式是把代码编译成bytecode(字节码)指令,然后由虚拟机来执行这些 bytecode 而 bytecode 长成这个样子:  b'|\x00\x00d\x01\x0 ...

  2. Python 字节码是什么

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

  3. Python字节码介绍

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

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

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

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

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

  6. Python字节码与解释器学习

    参考:http://blog.jobbole.com/55327/ http://blog.jobbole.com/56300/ http://blog.jobbole.com/56761/ 1. 在 ...

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

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

  8. Java向服务端转身 系统平台所对应的机器语言 虚拟CPU的机器语言字节码 bytecode

    小结: 1.虚拟CPU的模拟器:java虚拟机 JVM Java将虚拟机(VM)作为插件集成到浏览器中,将编译后的Java程序(Applet)在虚拟机上运行,这种技术 当初是为了增强浏览器的功能. J ...

  9. 使用uncompyle2直接反编译python字节码文件pyo/pyc

    update:在Mac OS X版的September 10, 2014版(5.0.9-1)中发现安装目录中的src.zip已更换位置至WingIDE.app/Contents/Resources/b ...

随机推荐

  1. luoguP1463:反素数ant(打表心得☆)

    题目描述 对于任何正整数x,其约数的个数记作g(x).例如g()=.g()=. 如果某个正整数x满足:g(x)>g(i) <i<x,则称x为反质数.例如,整数1,,,6等都是反质数. ...

  2. Python函数或者类的时间参数的默认值设为datetime.date.today()引起的问题

    定义了函数def main(start_date=datetime.date.today(), end_date=datetime.date.today()): pass 函数在项目启动后, end_ ...

  3. 深入理解linux内核-进程和程序

    进程描述符task_struct task_struct { //进程基本信息 pid 进程id号 tgid 线程组id号,与线程组领头线程pid号相同   getpid()返回该值 tasks in ...

  4. 【CodeForces】868F. Yet Another Minimization Problem

    原题链接 题目大意是有N个数,分成K段,每一段的花费是这个数里相同的数的数对个数,要求花费最小 如果只是区间里相同数对个数的话,莫队就够了 而这里是!边单调性优化边莫队(只是类似莫队)!而移动的次数和 ...

  5. LeetCode. 3的幂

    题目要求: 给定一个整数,写一个函数来判断它是否是 3 的幂次方. 示例: 输入: 27 输出: true 代码: class Solution { public: bool isPowerOfThr ...

  6. 【规律】Parentheses

    Parentheses 题目描述 Dave loves strings consisting only of '(' and ')'. Especially, he is interested in ...

  7. 【闭包】Pants On Fire

    Pants On Fire 题目描述 Donald and Mike are the leaders of the free world and haven’t yet (after half a y ...

  8. SSH框架结合案例构建配置

    ssh框架概述 SSH是 struts+spring+hibernate的一个集成框架,是目前比较流行的一种Web应用程序开源框架.区别于 Secure Shell . 集成SSH框架的系统从职责上分 ...

  9. Eureka 注册中心一直报Connect to localhost:8761 time out 的问题

    忽略了配置eureka.client.service-url.defaultZone而导致的异常,重新覆盖配置就好 client: fetch-registry: false register-wit ...

  10. [JZOJ5888]GCD生成树

    [JZOJ5888]GCD生成树 题目链接 gugugu 分析 对于N很小的情况,暴力Prim即可 对于值域很小的情况,我的想法与solution不太一样,将值相同的缩成一个点,\(O(w^2)\)预 ...