はい、Python はアクセスごとに新しいメソッド オブジェクトを作成します。これは、渡すラッパー オブジェクトを構築するためself
です。これをバインド メソッドと呼びます。
Python はこれを行うために記述子を使用します。関数オブジェクトには__get__
、クラスでアクセスされたときに呼び出されるメソッドがあります。
>>> A.__dict__['m'].__get__(A(), A)
<bound method A.m of <__main__.A object at 0x10c29bc10>>
>>> A().m
<bound method A.m of <__main__.A object at 0x10c3af450>>
Python は再利用できないことに注意してくださいA().m
。Python は非常に動的な言語であり、アクセスするという行為自体がより多くのコードをトリガーする可能性があり、次にアクセスしたときに返さ.m
れるものの動作が変わる可能性があります。A().m
@classmethod
とデコレータはこの@staticmethod
メカニズムを利用して、代わりにクラスにバインドされたメソッド オブジェクトと、バインドされていない単純な関数をそれぞれ返します。
>>> class Foo:
... @classmethod
... def bar(cls): pass
... @staticmethod
... def baz(): pass
...
>>> Foo.__dict__['bar'].__get__(Foo(), Foo)
<bound method type.bar of <class '__main__.Foo'>>
>>> Foo.__dict__['baz'].__get__(Foo(), Foo)
<function Foo.baz at 0x10c2a1f80>
>>> Foo().bar
<bound method type.bar of <class '__main__.Foo'>>
>>> Foo().baz
<function Foo.baz at 0x10c2a1f80>
詳細については、Python 記述子のハウツーを参照してください。
ただし、Python 3.7 では、毎回新しいメソッド オブジェクトを作成することを避けるために、現在の- opcode ペアを正確に置き換える新しいLOAD_METHOD
- opcode ペアが追加されています。この最適化は、 from withの実行パスを変換するため、「手動で」インスタンスを関数オブジェクトに直接渡します。見つかった属性が純粋な Python 関数オブジェクトでない場合、最適化は通常の属性アクセス パス (バインディング記述子を含む) に戻ります。CALL_METHOD
LOAD_ATTRIBUTE
CALL_FUNCTION
instance.foo()
type(instance).__dict__['foo'].__get__(instance, type(instance))()
type(instance).__dict__['foo'](instance)