首页 > 编程 > Python > 正文

详解python字节码

2020-01-04 15:55:59
字体:
来源:转载
供稿:网友

Python对不可变序列进行重复拼接操作效率会很低,因为每次都会生成一个新的对象,解释器需要把原来对象中的元素先复制到新的对象里,然后再追加新的元素。

但是CPython对字符串操作进行了优化,因为对字符串做+=操作实在是太普遍了。因此,初始化str时会预留出额外的可扩展空间,从而进行增量操作的时候不会有复制再追加的这个步骤。

通过字节码研究一下这个过程。

>>> s_code = 'a += "b"'>>> c = compile(s_code, '', 'exec')>>> c.co_codeb'e/x00/x00d/x00/x007Z/x00/x00d/x01/x00S'>>> c.co_names('a',)>>> c.co_consts('b', None)

得到的字节码是Bytes类型的。这里穿插一些Bytes类型的知识。

Bytes类型

b'e/x00/x00d/x00/x007Z/x00/x00d/x01/x00S',b表示是Bytes类型。Bytes以二进制字节序列的形式记录数据,每一个字符就代表一个字节(8位)。比如上面的e表示二进制0110 0101。部分ASCII码对照表如下图所示。

但是,不是所有的字节都是可显示的,甚至有些字节无法对应到ASCII码上(因为ASCII码只定义了128个字符,而一个字节有256个)。比如0000 0000对应的ASCII是不可显示的、0111 1111没有对应的ASCII码。

为了表示这些无法显示的字节,就引入了/x符号,其表示后续的字符为16进制。如,/x00表示16进制的00,也就是二进制的0000 0000。

至此,所有字节都可被表示。

字节码分析

回到开始的代码。为了显示方便,将b'e/x00/x00d/x00/x007Z/x00/x00d/x01/x00S'转为16进制来显示。

>>> c.co_code.hex()'650000640000375a000064010053'

通过opcode.opname函数可以得到操作码所对应的操作指令

>>> import opcode>>> opcode.opname[0x65]'LOAD_NAME'

因此,完整的字节码可以解释为(TOS即top-of-stack,栈顶元素):

字节:位置,功能65:0,LOAD_NAME0000:参数,将co_names[0]的值,即a的值,压入栈64:3,LOAD_CONST0000:参数,将co_consts[0],即'b',压入栈37:6,INPLACE_ADD,TOS = TOS1 + TOS5a:7,STORE_NAME0000:参数,co_names[0]=TOS,即将栈顶赋值给a64:10,LOAD_CONST0100:参数53:13,RETURN_VALUE,Returns with TOS to the caller of the function

实际上借助dis函数可以直接获得可读的字节码:

>>> import dis>>> dis.dis(s_code) 1      0 LOAD_NAME        0 (a)       3 LOAD_CONST        0 ('b')       6 INPLACE_ADD       7 STORE_NAME        0 (a)       10 LOAD_CONST        1 (None)       13 RETURN_VALUE

完整代码:

s_code = 'a += "b"'c = compile(s_code, '', 'exec')c.co_codec.co_namesc.co_constsc.co_code.hex()import disdis.dis(s_code)

非常失败,对比了string和tuple的赋值字节码,并没有看出string的优化…

以上就是本次关于python字节码的相关知识点,感谢你对VEVB武林网的支持。


注:相关教程知识阅读请移步到python教程频道。
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表