下面是一個簡單示例模塊代碼的反彙編。代碼對象是字節碼的只讀容器,它使用的常量和名稱以及有關局部變量數量,所需堆棧大小等的元數據。請注意,所有代碼對象都編譯爲常量。這些是在編譯時創建的。但是對象class A
和function test
在執行時被實例化(例如,當模塊被導入時)。
使課堂,BUILD_CLASS
取名字'A'
,該基地tuple
(object,)
,幷包含類的命名空間的屬性的dict
。這就像通過調用type(name, bases, dict)
來手動實例化類型。爲了使dict
,從代碼對象A
創建並調用一個函數。最後,類對象通過STORE_NAME
存儲在模塊名稱空間中。
在代碼對象A
中,self.z
作爲參數加載到堆棧上,作爲參數MAKE_FUNCTION
。字節碼操作LOAD_NAME
將在當前本地語言(即正在定義的類名稱空間),模塊全局變量和內建函數中搜索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:你的引擎收錄的例子(改寫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,)
如果參數不能帶有虛假值,則應該只寫'if a:a = self.z'或甚至'a = a或self.z'。 – danijar 2016-05-16 10:34:56
我想你的意思是'如果不是a:a = self.z' – BBischof 2017-04-07 18:05:05
這是一個標準的pythonic模式,即有一個默認的類屬性,用作可選方法屬性的回退?或者,更改'extendedClass' __init__構造函數以包含一個可選的'z = None'屬性,並且如果'None'將其設置爲方法中的某些默認值,並且不將其作爲方法參數?我只能認爲,如果一個硬編碼的類屬性真的是一個常量,並且被多個方法需要,那麼它很有用,否則爲什麼不只是將'z'限制在方法的範圍內,可能沒有類或對象財產呢? – Davos 2017-10-05 15:13:10