这是一个简单示例模块的反汇编代码。代码对象是字节码、它使用的常量和名称以及有关局部变量数量、所需堆栈大小等的元数据的只读容器。请注意,所有代码对象都被编译为常量。这些是在编译时创建的。但是对象class A 和function test 在执行时被实例化(例如,在导入模块时)。
要创建类,BUILD_CLASS 采用名称 'A'、基类 tuple (object,) 和包含类命名空间属性的 dict。这就像通过调用type(name, bases, dict) 手动实例化一个类型。为了创建dict,从代码对象A 创建一个函数并调用。最后,类对象通过STORE_NAME存储在模块命名空间中。
在代码对象A 中,self.z 作为MAKE_FUNCTION 的参数加载到堆栈中。字节码操作 @987654336@ 将在当前本地(即正在定义的类名称空间)、模块全局变量和内置函数中搜索 self。如果self 没有在全局或内置范围中定义,这显然会失败;它显然没有在本地范围内定义。
但是,如果它成功了,那么将使用(self.z,) 作为其__defaults__ 属性创建该函数,然后将其存储到本地名称test。
>>> code = compile('''
... class A(object):
... def test(self, a=self.z): pass
... ''', '<input>', 'exec')
>>> dis.dis(code)
2 0 LOAD_CONST 0 ('A')
3 LOAD_NAME 0 (object)
6 BUILD_TUPLE 1
9 LOAD_CONST 1 (<code object A ...>)
12 MAKE_FUNCTION 0
15 CALL_FUNCTION 0
18 BUILD_CLASS
19 STORE_NAME 1 (A)
22 LOAD_CONST 2 (None)
25 RETURN_VALUE
>>> dis.dis(code.co_consts[1]) # code object A
2 0 LOAD_NAME 0 (__name__)
3 STORE_NAME 1 (__module__)
3 6 LOAD_NAME 2 (self)
9 LOAD_ATTR 3 (z)
12 LOAD_CONST 0 (<code object test ...>)
15 MAKE_FUNCTION 1
18 STORE_NAME 4 (test)
21 LOAD_LOCALS
22 RETURN_VALUE
@uselpa: 您的 pastebin 示例(为 2.x 重写):
>>> code = compile('''
... default = 1
... class Cl(object):
... def __init__(self, a=default):
... print a
... Cl()
... default = 2
... Cl()
... ''', '<input>', 'exec')
>>> dis.dis(code)
2 0 LOAD_CONST 0 (1)
3 STORE_NAME 0 (default)
3 6 LOAD_CONST 1 ('Cl')
9 LOAD_NAME 1 (object)
12 BUILD_TUPLE 1
15 LOAD_CONST 2 (<code object Cl ...>)
18 MAKE_FUNCTION 0
21 CALL_FUNCTION 0
24 BUILD_CLASS
25 STORE_NAME 2 (Cl)
6 28 LOAD_NAME 2 (Cl)
31 CALL_FUNCTION 0
34 POP_TOP
7 35 LOAD_CONST 3 (2)
38 STORE_NAME 0 (default)
8 41 LOAD_NAME 2 (Cl)
44 CALL_FUNCTION 0
47 POP_TOP
48 LOAD_CONST 4 (None)
51 RETURN_VALUE
如您所见,类对象Cl(和函数对象__init__)仅被实例化并存储到本地名称'Cl' 一次。该模块在运行时按顺序执行,因此随后重新绑定名称 default 将不会影响 __init__ 中的默认值。
您可以使用先前编译的代码和新的默认值动态实例化一个新函数:
>>> default = 1
>>> class Cl(object):
... def __init__(self, a=default):
... print a
...
>>> from types import FunctionType
>>> default = 2
>>> Cl.__init__ = FunctionType(
... Cl.__init__.__code__, globals(), '__init__', (default,), None)
>>> c = Cl()
2
这会重用来自__init__.__code__ 的已编译代码对象来创建一个带有新的__defaults__ 元组的函数:
>>> Cl.__init__.__defaults__
(2,)