18

なぜ次のことが起こるのか理解できません。関数がメソッドであるかどうかをチェックする以外は何もしないデコレータを使用しています。私はPythonのメソッドが何であるかを理解していると思っていましたが、明らかにそうではありません:

import inspect

def deco(f):
    def g(*args):
        print inspect.ismethod(f)
        return f(*args)
    return g

class Adder:
    @deco
    def __call__(self, a):
        return a + 1

class Adder2:
    def __call__(self, a):
        return a + 2
Adder2.__call__ = deco(Adder2.__call__)

ここで、次を実行します。

>>> a = Adder()
>>> a(1)
False
2
>>> a2 = Adder2()
>>> a2(1)    
True
3

このコードで True が 2 回出力されることを期待します。

では、Adder2 のように関数を手動で装飾することは、@deco 関数を介して装飾することとまったく同じではありませんか?

誰かがとても喜んで、なぜこれが起こるのか説明できますか?

4

2 に答える 2

14

メソッドは、クラスに関連付けられている関数です。メソッドは、すでに定義されているクラスからメソッドを取得する場合にのみ作成されます。メソッドは関数のラッパーであり、クラスへの参照(およびオプションでインスタンスへの参照)もあります。

最初のケースで起こることは次のとおりです。Pythonはクラス定義をコンパイルしますAdder。デコレータの定義と関数を見つけます。デコレータに関数が渡され、新しい関数が返されます。その関数はクラス定義に追加されます(クラスに格納されます__dict__)。この間ずっと、メソッドではなくPython関数を扱っています。それは後で起こります。

次にを呼び出すa(1)と、ルックアップにより、インスタンスにはがないことがわかります__call__が、Adderクラスにはあるため、を使用して取得されます__getattribute__()。これにより、記述子である関数decoデコレータ)が検出され、そのメソッドが呼び出されます(つまり、バインドされたメソッドが返され、呼び出されて値が渡されます。呼び出されたときにNoneではないため、メソッドはバインドされます。デコレータクラス構築時に関数をラップした、は、最初にラップされていない関数が渡されたために出力されます。__get__()Adder.__call__.__get__(a, Adder))1instance__get__()False

ただし、2番目のケースでは、メソッドを取得し(ここでも、装飾されていない関数を__getattribute__()呼び出すことにより)、今回はバインドされていません(インスタンスがないため、クラスのみが渡されます(完全な呼び出しは)) 。次に、そのメソッドを装飾します。Trueを出力するようになりました。__get__()Adder2.__call____get__()Adder2.__call__.__get__(None, Adder2)ismethod()

Python 3では、後者の場合が変わることに注意してください。Python 3では、バインドされていないメソッドの概念はなくなり、関数とバインドされたメソッドのみが使用されます。したがって、「バインドされた」という用語は完全に削除されます。2番目のケースも、その場合は関数を返すように出力Falseされます。Adder2.__call__.__get__(None, Adder2)

于 2012-09-19T10:52:29.190 に答える
6

クラス定義内に__call__は、メソッドではなく関数があります。を使用するなど、属性ルックアップを介して関数にアクセスする動作(たとえば、ドット構文を使用)Adder2.__call__は、バインドされていないメソッドを返します。そしてa2.__call__、バインドされたメソッドを返します(にselfバインドされていa2ます)。

Python3では、の概念unbound methodが削除されていることに注意してください。そこにAdder2.__call__は機能もあります。

于 2012-09-19T10:51:23.860 に答える