字节码bytecode

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

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

前置知识

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

例子:

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

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

code object 代码对象

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

  1. >>> type(hello.__code__)
  2. <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__

例子:

  1. >>> hello.__code__.co_consts
  2. (None, 'Hello World!')
  3.  
  4. >>> hello.__code__.co_varnames
  5. ()
  6. >>> hello.__code__.co_names
  7. ('print',)

dis模块

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

  1. >>> dis.dis("hello()")
  2. 1 0 LOAD_NAME 0 (hello)
  3. 2 CALL_FUNCTION 0
  4. 4 RETURN_VALUE
  5. >>>
  1. >>> dis.dis(hello)
  2. 2 0 LOAD_GLOBAL 0 (print)
  3. 2 LOAD_CONST 1 ('Hello World!')
  4. 4 CALL_FUNCTION 1
  5. 6 POP_TOP
  6. 8 LOAD_CONST 0 (None)
  7. 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

  1. >>> dis.opname[0x65]
  2. 'LOAD_NAME'
  3. >>> dis.opname[101]
  4. 'LOAD_NAME' 

解释:

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

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

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

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

例子2

  1. >>> x = dis.Bytecode(hello)
  2. >>> x
  3. Bytecode(<function hello at 0x106594b80>)
  4. >>> for instr in x:
  5. ... print(instr.opname)
  6. ...
  7. LOAD_GLOBAL
  8. LOAD_CONST
  9. CALL_FUNCTION
  10. POP_TOP
  11. LOAD_CONST
  12. RETURN_VALUE

解释:

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

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

  1. >>> for instr in x:
  2. ... print(instr.opcode)
  3. ...
  4. 116
  5. 100
  6. 131
  7. 1
  8. 100
  9. 83

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

  1. >>> hello.__code__
  2. <code object hello at 0x1066683a0, file "<stdin>", line 1>
  3. >>> hello.__code__.co_code
  4. b't\x00d\x01\x83\x01\x01\x00d\x00S\x00'
  5. >>> list(hello.__code__.co_code)
  6. [116, 0, 100, 1, 131, 1, 1, 0, 100, 0, 83, 0]
  7. >>> dis.opname[116]
  8. 'LOAD_GLOBAL'
  9. >>> dis.dis(hello)
  10. 2 0 LOAD_GLOBAL 0 (print)
  11. 2 LOAD_CONST 1 ('Hello World!')
  12. 4 CALL_FUNCTION 1
  13. 6 POP_TOP
  14. 8 LOAD_CONST 0 (None)
  15. 10 RETURN_VALUE
  16. >>> dis.opname[1]
  17. '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. printf特殊用法

    printf("%*.*lf\n", a, b, c); //表示a宽距,保留b位小数 用这种方法可以通过输入控制a和b

  2. [转帖]新一代IBM Z14主机技术介绍

    新一代IBM Z14主机技术介绍 https://cloud.tencent.com/developer/news/268909 IBM最新的已经有IBM Z15 主机了.. 文章来源:企鹅号 - 云 ...

  3. 数据结构 -- 哈希表(hash table)

    简介   哈希表(Hash table,也叫散列表),是根据关键码值(Key value)而直接进行访问的数据结构.也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度.这个映射函 ...

  4. 笔记-4:mysql数据查询

    1.创建查询表 1.1 创建班级表 含义 字段名 数据类型 宽度 班级编号 classNo 字符型 6 班级名称 className 字符型 20 所属院系 department 字符型 30 年级 ...

  5. 关于InnoDB存储引擎 text blob 大字段的存储和优化

    最近在数据库优化的时候,看到一些表在设计上使用了text或者blob的字段,单表的存储空间已经达到了近100G,这种情况再去改变和优化就非常难了 一.简介 为了清楚大字段对性能的影响,我们必须要知道i ...

  6. “Using 声明”在 C# 7.3 中不可用。请使用 8.0 或更高的语言版本。

    Core3.0升级至3.1时候报错:“Using 声明”在 C# 7.3 中不可用.请使用 8.0 或更高的语言版本. 参照微软文档:https://docs.microsoft.com/zh-cn/ ...

  7. Nginx server配置

    项目一般都需要前后端的配置,用二级域名把它区分开:首先在nginx.conf:里面加一句话: http{ #这里面有很多其他的配置 如:gzip FastCGI等等 include vhosts/*. ...

  8. hdu 4857 反向拓扑问题

    尤其要注意拓扑的分层问题 不难理解 就是不怎么好想到 拓扑的思路这里就不累述了 #include <iostream> #include <cstdio> #include & ...

  9. (四)输入参数与输出类型为复杂类型的web服务

    一. 服务端发布服务 1.1 定义复杂类型:UserBean.java package service; public class UserBean { private String userId; ...

  10. Java CountingSort

    Java CountingSort /** * <html> * <body> * <P> Copyright 1994-2018 JasonInternation ...