簡単なサンプルモジュールのコードの逆アセンブルは次のとおりです。コードオブジェクトは、バイトコード、それが使用する定数と名前、およびローカル変数の数、必要なスタックサイズなどに関するメタデータの読み取り専用コンテナです。すべてのコードオブジェクトが定数としてコンパイルされていることに注意してください。これらはコンパイル時に作成されます。ただし、オブジェクトclass A
とfunction test
は実行時にインスタンス化されます(モジュールのインポート時など)。
クラスを作成するにBUILD_CLASS
は、名前'A'
、bases tuple
(object,)
、およびdict
クラス名前空間の属性を含むaを取ります。これは、を呼び出してタイプを手動でインスタンス化するようなものtype(name, bases, dict)
です。を作成するにはdict
、コードオブジェクトから関数を作成しA
て呼び出します。最後に、クラスオブジェクトはを介してモジュール名前空間に格納されますSTORE_NAME
。
コードオブジェクトA
でself.z
は、はへの引数としてスタックにロードされますMAKE_FUNCTION
。バイトコードopは、現在のローカル(つまり、定義されているクラス名前空間)、モジュールグローバル、およびビルトイン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'
に1回だけ保存されます。モジュールは実行時に順次実行されるため、後で名前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,)