【问题标题】:Why does a class definition always produce the same bytecode?为什么类定义总是产生相同的字节码?
【发布时间】:2014-10-03 15:29:04
【问题描述】:

说我愿意:

#!/usr/bin/env python
# encoding: utf-8

class A(object):
    pass

现在我拆解它:

python -m dis test0.py 
  4           0 LOAD_CONST               0 ('A')
              3 LOAD_NAME                0 (object)
              6 BUILD_TUPLE              1
              9 LOAD_CONST               1 (<code object A at 0x1004ebb30, file "test0.py", line 4>)
             12 MAKE_FUNCTION            0
             15 CALL_FUNCTION            0
             18 BUILD_CLASS         
             19 STORE_NAME               1 (A)
             22 LOAD_CONST               2 (None)
             25 RETURN_VALUE        

现在我在类定义中添加一些语句:

#!/usr/bin/env python
# encoding: utf-8

class A(object):
    print 'hello'
    1+1
    pass

然后我再次拆解:

  4           0 LOAD_CONST               0 ('A')
              3 LOAD_NAME                0 (object)
              6 BUILD_TUPLE              1
              9 LOAD_CONST               1 (<code object A at 0x1004ebb30, file "test0.py", line 4>)
             12 MAKE_FUNCTION            0
             15 CALL_FUNCTION            0
             18 BUILD_CLASS         
             19 STORE_NAME               1 (A)
             22 LOAD_CONST               2 (None)
             25 RETURN_VALUE        

什么新语句没有出现在新字节码中?

【问题讨论】:

    标签: python bytecode disassembly python-internals


    【解决方案1】:

    新语句存储在嵌套字节码中。您可以在反汇编中看到 另一个代码对象 已加载:

          9 LOAD_CONST               1 (<code object A at 0x1004ebb30, file "test0.py", line 4>)
    

    您需要改为检查 那个 代码对象。那是因为类体就像函数对象一样被执行,调用产生的本地命名空间随后被用来形成类成员。

    演示:

    >>> import dis
    >>> def wrapper():
    ...     class A(object):
    ...         pass
    ... 
    >>> dis.dis(wrapper)
      2           0 LOAD_CONST               1 ('A')
                  3 LOAD_GLOBAL              0 (object)
                  6 BUILD_TUPLE              1
                  9 LOAD_CONST               2 (<code object A at 0x104b99930, file "<stdin>", line 2>)
                 12 MAKE_FUNCTION            0
                 15 CALL_FUNCTION            0
                 18 BUILD_CLASS         
                 19 STORE_FAST               0 (A)
                 22 LOAD_CONST               0 (None)
                 25 RETURN_VALUE        
    >>> dis.dis(wrapper.__code__.co_consts[2])
      2           0 LOAD_NAME                0 (__name__)
                  3 STORE_NAME               1 (__module__)
    
      3           6 LOAD_LOCALS         
                  7 RETURN_VALUE        
    

    这与您的第一个示例相同;类体通过wrapper.__code__.co_consts 元组访问,这是LOAD_CONST 字节码所指的;索引为2

    现在我们可以添加一个类体:

    >>> def wrapper():
    ...     class A(object):
    ...         print 'hello'
    ...         1+1
    ...         pass
    ... 
    >>> dis.dis(wrapper)
      2           0 LOAD_CONST               1 ('A')
                  3 LOAD_GLOBAL              0 (object)
                  6 BUILD_TUPLE              1
                  9 LOAD_CONST               2 (<code object A at 0x104b4adb0, file "<stdin>", line 2>)
                 12 MAKE_FUNCTION            0
                 15 CALL_FUNCTION            0
                 18 BUILD_CLASS         
                 19 STORE_FAST               0 (A)
                 22 LOAD_CONST               0 (None)
                 25 RETURN_VALUE        
    >>> dis.dis(wrapper.__code__.co_consts[2])
      2           0 LOAD_NAME                0 (__name__)
                  3 STORE_NAME               1 (__module__)
    
      3           6 LOAD_CONST               0 ('hello')
                  9 PRINT_ITEM          
                 10 PRINT_NEWLINE       
    
      4          11 LOAD_CONST               2 (2)
                 14 POP_TOP             
    
      5          15 LOAD_LOCALS         
                 16 RETURN_VALUE        
    

    现在类体出现了;我们可以看到加载类体时将执行的字节码。

    值得注意的是为每个类主体执行的LOAD_NAMESTORE_NAME 字节码;它们检索模块名称并将其存储为新的本地名称__module__,以便您的类在创建后以__module__ 属性结束。

    LOAD_LOCALS 字节码然后收集此“函数”中生成的所有本地名称并将其返回给调用者,以便BUILD_CLASS 字节码可以与'A' 字符串和object 基一起使用元组(使用BUILD_TUPLE 创建)可以生成新的类对象。

    【讨论】:

    • 甜蜜。不知道那个。有趣的问题和一个很好的答案。 (+1)
    猜你喜欢
    • 2011-06-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-08-09
    • 2022-11-03
    • 2018-01-04
    • 2011-12-24
    相关资源
    最近更新 更多