28

私はpythonが初めて__func__で、python 2.7のことをよく理解していません。

次のようなクラスをいつ定義するか知っています。

class Foo:
    def f(self, arg):
        print arg

Foo().f('a')または のいずれかを使用して、Foo.f(Foo(), 'a')このメソッドを呼び出すことができます。ただし、このメソッドを で呼び出すことはできませんFoo.f(Foo, 'a')Foo.f.__func__(Foo, 'a')しかし、偶然にも、 またはを使用Foo.f.__func__(1, 'a')して同じ結果を得る ことができることがわかりました。

Foo.fFoo().fおよびの値を出力しますが、Foo.f.__func__それらはすべて異なります。ただし、定義にはコードが 1 つしかありません。上記のコードが実際にどのように機能するか、特に を説明できるのは誰__func__ですか? 私は今、本当に混乱しています。

4

1 に答える 1

54

アクセスしたとき、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_ATTRIBUTECALL_FUNCTIONinstance.foo()type(instance).__dict__['foo'].__get__(instance, type(instance))()type(instance).__dict__['foo'](instance)

于 2012-10-17T13:24:03.983 に答える