2017-05-21 10 views
2

Pythonのクラスデコレータがどのように機能するかを理解することに問題があります。このケースでは、再帰関数(Greatest Common Divisorの検索)が何回呼び出されたかを数えるデコレータを作成したいと思います。クラスデコレータがPythonでどのように機能するかを理解する

class TrackCalls(object): 

def __init__(self, func): 
    self.func = func 
    self.calls = 0 

def __call__(self,*args,**kwargs): 
    self.calls += 1 
    return self.func(*args, **kwargs) 

def called(self): 
    return self.calls 

と機能::私はデコレータ持っ

@TrackCalls 
def NWD(a, b): 

    if a > b: 
     return NWD(a-b, b) 
    elif b > a: 
     return NWD(a, b-a) 
    else: 
     return a 

をし、私はそれらを呼び出す:

print(NWD(60,25)) #5 
print(NWD.called()) #6 

正確NWD機能で何が起こっていますか?私の知る限り、デコレータは関数を呼び出して別の関数を作ります。この場合、TrackCallsは関数をとり、TrackCallsクラスのオブジェクトを作成してから、NWD.called()を呼び出すと、私は基本的にTrackCallsオブジェクトのメソッドを呼び出します。例えば私が走ったとき。 NWD(5,25)前の呼び出しの後では11になるので、NWDを呼び出すたびに、何らかの静的変数を持つTrackCallsオブジェクトを呼び出すように見えます。私が同じデコレータで別の再帰関数を飾っていたら、calls変数を共有しますか?

+4

試してみてください! – jonrsharpe

+0

私は正直なところ、この作品が少し驚いています。私はそれが再帰呼び出しを数えないと思ったでしょう。 –

答えて

3

私はあなたの混乱は、パイロットのダック型の概念にさかのぼることができると思います。これは、外部の観点から、メソッドを持つクラスの [それはfuncと呼ぶ]とオブジェクト(=インスタンス)[それをobjと呼ぶことができます]の間に違いがないという考え方を指します。どちらもcallableです。つまり、括弧内の引数には、それぞれfunc(args)obj(args)のような戻り値があります。原因の違いは、オブジェクトとしてobjに状態があることです。つまり、インスタンス変数(フィールドと呼ばれることもあります)があります。

例では、callsfuncは、TrackCallsクラスのフィールドです。 の機能を@TrackCallsで飾ることにより、NWDTrackCallsでラップします。つまり、名前NWDは、NWD = TrackCalls(NWD)と同等のTrackCallsのインスタンスに置き換えられます。新しいインスタンスは呼び出し可能であり、元の関数のように動作しますが、オブジェクトであるため状態も持ち、__call__メソッドの呼び出しを数えることができます。計算自体はNWDの元の実装に委譲され、funcフィールドに格納されます。あなたはTrackCallsを飾る各関数はTrackCallsの新しいインスタンスに独自の状態でそれぞれなり、あなたの質問に答えるために

。しかし、1つの装飾された機能への複数の呼び出しは、の機能の状態を共有します。

+0

私はしばらくこのことに苦労してきましたが、あなたは私にそれを明確にしました、ありがとうございます:) –

関連する問題