Python 的执行方式,字节码 bytecode

这里有个博客讲 Python 内部机制,已经有一些中文翻译

可能因为我用的Python 3.5,例子跑起来有些不一样。

此外,我又查了其他一些参考资料,总结如下:

Python 的执行方式

先看一个比较详细的步骤分解:

>>> a = "hello"

输入这行代码之后,你一按回车,Python就会执行四步操作:

1  lexing: 词法分析,就是把一个句子分解成 token。大致来说,就是用str.split()可以实现的功能。

2  parsing:解析,就是把这些 token 组装成一个逻辑结构。

3  compiling:编译,把这个逻辑结构转化成一个或者多个code object (代码对象)

4  interpreting:解释,执行每个code object 代表的代码。

还有一种比较简单的说法是这样的:

Python 程序的执行过程就是,它先把代码编译成 bytecode (字节码)指令,交给虚拟机,逐条执行 bytecode 指令。

这两种说法基本上是一样的,只是存在一个code object 和 bytecode 的差异。那么它们之间存在怎样的关系呢?

从操作上说,bytecode 可以在 code object 的属性中找到。

分清function object、code object ,以及 bytecode

>>> def double(a):
    return a*2

>>> double
<function double at 0x000001D8082E48C8>

为什么粘贴到这里对齐会是这样?先不管了。

从上面可以看到,定义一个函数之后,它就成了一个function object (函数对象)。只要不使用函数调用符号——也就是小括号——这个函数就不会执行。

但是它已经被编译了,可以通过这个function object 的__code__ 属性找到它的 code object

>>> def double(a):
return a*2

>>> double
<function double at 0x00000169C5F7FF28>

>>> type (double)
<class ‘function‘>

>>> double.__code__  #找到double 函数对象的 code object

<code object double at 0x00000169C5F36AE0, file "<pyshell#58>", line 1>

>>> type(double.__code__)
<class ‘code‘>

最后一行可以看到, code object 的类型是 ‘code’

前面说过,bytecode 是 code object 的一个属性的值。这个属性名为 co_code

在 code object 的co_code属性里面,存放了一个字符串,它就是bytecode 序列:

>>> double.__code__.co_code
b‘|\x00\x00d\x01\x00\x14S‘

bytecode 是几个意思?

>>> double.__code__.co_code
b‘|\x00\x00d\x01\x00\x14S‘
>>> type(double.__code__.co_code)
<class ‘bytes‘>
>>> len(double.__code__.co_code)
8

它的类型是‘bytes’ ,长度是8。 你可能觉得奇怪,这个8是怎么数出来的?

注意: Python 3 中 str 类型大致相当于 Python 2 中的unicode 类型,但是 Python 3 中 bytes 类型并不是Python 2 中的 str 类型改了个名字。

bytes 是二进制序列,它的每个元素都是一个整数,值在0-255之间。

>>> for i in double.__code__.co_code:
    print (i, end="    ")

124    0    0    100    1    0    20    83
>>> double.__code__.co_code[-1]
83 

是不是正好8个元素? 第一个是124,最后一个是83

>>> chr(124)
‘|‘
>>> chr(83)
‘S‘

这里是一个很让人迷惑的地方:为什么要把 ‘|‘ 、‘S‘这样的字符和 x00 这样的十六进制表示混在一起?这其实只是Python 在显示 bytes 类型的对象给你看的时候,会把ASCII 码范围内的十六进制元素

这段 bytecode 由8个整数组成,每个整数都有深刻的含义,不亚于昆汀的《八恶人》

可能你已经猜到了,它们是一个字典中的键,我们需要的当然是这个字典中的值,它一定是指令的名字

这个字典就在文件opcode.py里。

def_op(‘LOAD_CONST‘, 100)       # Index in const list
def_op(‘BUILD_TUPLE‘, 102)      # Number of tuple items
def_op(‘BUILD_LIST‘, 103)       # Number of list items
def_op(‘BUILD_SET‘, 104)        # Number of set items

所以你猜错了,这些数字才是键值对中的值,我们需要的是键——它们确实是指令的名字。

有一个方法帮你找出值 (不要忘了先 import opcode)

>>> opcode.opmap["LOAD_FAST"]
124
>>> opcode.opmap["RETURN_VALUE"]
83

找值好像没什么意思嘛,我们更需要的是找键的方法。

>>> opcode.opname[83]
‘RETURN_VALUE‘
>>> opcode.opname[124]
‘LOAD_FAST‘

其实找键也不需要,Python 有个dis反汇编工具可以用 (不要忘了先 import dis)
>>> dis.dis(double)
  2           0 LOAD_FAST                0 (a)
              3 LOAD_CONST               1 (2)
              6 BINARY_MULTIPLY
              7 RETURN_VALUE
再回头看看那8个整数(这就是 bytecode的意思 —— 用for循环把 bytecode 迭代一遍得到的数字,代表一个指令序列 )

      124    0    0    100    1    0    20    83  
偏移量  0     1    2     3     4    5    6     7 

所以第二列的0 3 6 7 是什么呢,就是这个偏移量啊

明天再写~~
时间: 2024-10-12 14:56:42

Python 的执行方式,字节码 bytecode的相关文章

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

上次写到,Python 的执行方式是把代码编译成bytecode(字节码)指令,然后由虚拟机来执行这些 bytecode 而 bytecode 长成这个样子:  b'|\x00\x00d\x01\x00\x14S' .显然这个样子适合机器看,不适合人类看. 虽然你可以通过查字典的方式,手动把这段 bytecode 编写成人类可以看得懂的样子, 但是这么劳累的事情,为什么要自己亲手来做呢,让你的男仆机器来做不就好了吗. Python 的反汇编工具 dis 就可以办到这件事.下面用绚丽的紫色来对di

Python 字节码bytecode

字节码bytecode dis模块是Python字节码反汇编器.通过反汇编支持Cpython的字节码分析. 前置知识 在看字节码之前,先要了解一下code object和frame object,它们在datamodel.html中有介绍 例子: >>> import dis >>> def hello(): ... print('Hello World!') ... >>> hello.__code__ <code object hello a

Python的字符串与字节码转换

一张图弄懂python的字符串与字节码转换 原文地址:https://www.cnblogs.com/steven9898/p/11329474.html

Python 文件编译为字节码的方法

一般情况下 python 不需要手动编译字节码.但是如果不想直接 release 源代码给其他人,将文件编译成字节码,可以实现一定程度的信息隐藏. 1) 使用模块 py_compile 编译一个单文件 import py_compile py_compile.compile('./demo.py') 2) 使用模块 compileall 递归的编译一个文件包 import compileall compileall.compile_dir('./test') 3) 使用模块 compileall

java的字节码bytecode

字节码名字的由来 字节码以一个字节即8bit为最小单位储存:字节码是java程序编译后的结果:字节码是一组8位字节为基础单位的二进制流 Java从源文件到执行的过程. 如何阅读JAVA字节码 原文地址:https://www.cnblogs.com/shengulong/p/11711423.html

python字节码(转)

了解 Python 字节码是什么,Python 如何使用它来执行你的代码,以及知道它是如何帮到你的. 如果你曾经编写过 Python,或者只是使用过 Python,你或许经常会看到 Python 源代码文件--它们的名字以 .py 结尾.你可能还看到过其它类型的文件,比如以 .pyc 结尾的文件,或许你可能听说过它们就是 Python 的 "字节码bytecode" 文件.(在 Python 3 上这些可能不容易看到 -- 因为它们与你的 .py 文件不在同一个目录下,它们在一个叫 _

《python源码剖析-字节码和虚拟机》

|   分类于 python源码剖析  | https://fanchao01.github.io/blog/categories/python%E6%BA%90%E7%A0%81%E5%89%96%E6%9E%90/ https://fanchao01.github.io/blog/2014/12/26/python-GIL/ python源码剖析-字节码和虚拟机 发表于 2016-11-13   |   分类于 python源码剖析  | Python会将代码先编译成字节码,然后在虚拟机中动

深入JAVA虚拟机之字节码执行引擎

前言:class文件结构.类加载机制.类加载器.运行时数据区这四个java技术体系中非常重要的知识,学习完了这些以后,我们知道一个类是通过类加载器加载到虚拟机,存储到运行时数据区,而且我们也知道了我们方法体内的代码被编译成字节码保存在方法表中的code属性中,那么虚拟机又是怎么执行这些代码的,得出方法输出结果的呢?这一节我们就要来学习,关于虚拟机字节码执行引擎的相关知识.通过这章节的学习,我们要掌握一下知识点: 1.运行时栈帧结构 2.方法调用 3.基于栈的字节码执行引擎 运行时栈帧结构 栈帧是

lua执行字节码的过程介绍

前面一篇文章中介绍了lua给下面代码生成最终的字节码的整个过程,这次我们来看看lua vm执行这些字节码的过程. 1 foo = "bar" 2 local a, b = "a", "b" 3 foo = a 生成的字节码如下所示: 之前lua是在luaY_parser函数(入口)中完成了lua脚本的解析生成字节码的整个过程的,在生成了main func(过程见“lua解析赋值类型代码的过程“)后luaY_parser会返回一个Proto结构体指