ここでは、簡単なサンプルモジュールのコードの逆アセンブリについて説明します。コードオブジェクトは、バイトコード、それが使用する定数および名前、ローカル変数の数、必要なスタックサイズなどに関するメタデータの読み取り専用コンテナです。すべてのコードオブジェクトは定数としてコンパイルされます。これらはコンパイル時に作成されます。しかし、オブジェクトclass A
とfunction test
は、実行時にインスタンス化されます(モジュールがインポートされるときなど)。クラスを作るために
、BUILD_CLASS
は、クラスの名前空間の属性が含まれている拠点tuple
(object,)
、名前'A'
を取り、dict
。これは、type(name, bases, dict)
を呼び出すことによって手動で型をインスタンス化するのと同じです。 dict
を作成するには、コードオブジェクトA
から関数を作成して呼び出す。最後に、クラスオブジェクトはSTORE_NAME
を介してモジュール名前空間に格納されます。
コードオブジェクトA
では、の引数としてself.z
がスタックにロードされます。バイトコードop LOAD_NAME
は、現在のローカル(つまり、定義されているクラスの名前空間)内のself
、モジュールのグローバル、および組み込みコードを検索します。 self
がglobalまたはbuiltinsスコープで定義されていない場合、これは明らかに失敗します。明らかにローカルスコープでは定義されていません。それが成功しなかった場合は
、しかし、機能はその__defaults__
属性として(self.z,)
で作成し、ローカル名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__
には影響しません。新しい__defaults__
タプルで関数を作成する。これは、__init__.__code__
からすでにコンパイルされたコードオブジェクトを再利用する
>>> 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
:あなたは、動的にあらかじめコンパイルされたコードと新しいデフォルト値を使用して新しい機能をインスタンス化でき
>>> Cl.__init__.__defaults__
(2,)
引数が偽値をとることができない場合は、 'if a:a = self.z'または' a = a or self.z'と書いてください。 – danijar
私はあなたが 'a not:a = self.z'を意味すると思います。 – BBischof
これは標準的なpythonicパターンですか?つまり、オプションのメソッドプロパティからのフォールバックとして使用されるデフォルトのクラスプロパティですか?あるいは、オプションの 'z = None'プロパティを含むように' extendedClass' __init__コンストラクタを変更し、 'None'をメソッド内部のデフォルトに設定し、それをメソッドパラメータとして持たないようにする方が良いでしょうか?ハードコーディングされたクラスプロパティを持つことは本当に定数で、複数のメソッドで必要な場合に便利です。そうでなければ 'z 'をメソッドのスコープに制限するだけでなく、クラスやオブジェクトがないすべてのプロパティですか? – Davos