アクセスしたとき、Foo.f
またはメソッドが返されたとき。最初のケースではバインドされておらず、2 番目のケースではバインドされています。Python メソッドは基本的に、メソッドであるクラスへの参照も保持する関数のラッパーです。バインドされると、インスタンスへの参照も保持されます。Foo().f
メソッドを呼び出すと、渡された最初の引数がインスタンスであることを確認するために型チェックが行われます (参照されるクラスのインスタンス、またはそのクラスのサブクラスである必要があります)。メソッドがバインドされると、最初の引数が提供されます。バインドされていないメソッドでは、自分で提供します。
__func__
ラップされた関数への単なる参照である属性を持つのは、このメソッド オブジェクトです。メソッドを呼び出す代わりに基になる関数にアクセスすることで、型チェックを削除し、必要なものを最初の引数として渡すことができます。関数は引数の型を気にしませんが、メソッドは気にします。
Python 3 ではこれが変更されていることに注意してください。Foo.f
バインドされていないメソッドではなく、関数を返すだけです。Foo().f
まだバインドされているメソッドを返しますが、バインドされていないメソッドを作成する方法はもうありません。
内部的には、各関数オブジェクトには__get__
メソッドがあり、これがメソッド オブジェクトを返します。
>>> class Foo(object):
... def f(self): pass
...
>>> Foo.f
<unbound method Foo.f>
>>> Foo().f
<bound method Foo.f of <__main__.Foo object at 0x11046bc10>>
>>> Foo.__dict__['f']
<function f at 0x110450230>
>>> Foo.f.__func__
<function f at 0x110450230>
>>> Foo.f.__func__.__get__(Foo(), Foo)
<bound method Foo.f of <__main__.Foo object at 0x11046bc50>>
>>> Foo.f.__func__.__get__(None, Foo)
<unbound method Foo.f>
これは最も効率的なコードパスではないため、Python 3.7 は新しいLOAD_METHOD
- opcode ペアを追加し、現在の- opcode ペアを正確にCALL_METHOD
置き換えて、毎回新しいメソッド オブジェクトを作成しないようにします。この最適化は、 from withの実行パスを変換するため、「手動で」インスタンスを関数オブジェクトに直接渡します。これにより、既存のマイクロベンチマークで約 20% の時間を節約できます。LOAD_ATTRIBUTE
CALL_FUNCTION
instance.foo()
type(instance).__dict__['foo'].__get__(instance, type(instance))()
type(instance).__dict__['foo'](instance)