zoukankan      html  css  js  c++  java
  • 理解 Python 的执行方式,与字节码 bytecode 玩耍 (下)

    上次写到,Python 的执行方式是把代码编译成bytecode(字节码)指令,然后由虚拟机来执行这些 bytecode

    而 bytecode 长成这个样子:  b'|x00x00dx01x00x14S' 。显然这个样子适合机器看,不适合人类看。 

    虽然你可以通过查字典的方式,手动把这段 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'|x00x00dx01x00x14S' 这种东西就行了),

    比如第一个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'|x00x00dx01x00x14S' 其实是8个整数

    >>> double.__code__.co_code
    b'|x00x00dx01x00x14S'
    >>> 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,这里就完全倒过来,添枝加叶,逆流而上了。

  • 相关阅读:
    SQLServer 2008数据库查看死锁、堵塞的SQL语句
    Jmeter(三)简单的HTTP请求(非录制)
    watir中不能打开页面中的URL超链接解决办法
    我要搬博客到这里来,请协助
    Jmeter(一)精简测试脚本
    性能测试机中存在大量的TIME_WAIT状态的连接,影响并发压力的发起
    ruby+watir随机而不重复获取Menu菜单的元素
    Eclipse中安装Ruby的插件org.rubypeople.rdt
    TCP连接各状态数量、以及TCP各状态变迁流程
    ruby+watirwatir3.0上实现快照/截图
  • 原文地址:https://www.cnblogs.com/hello2764/p/5466945.html
Copyright © 2011-2022 走看看