2016-08-22 4 views
1

返された後にcount_calls_badのバージョンに追加された関数属性が保持されないのはなぜですか?(デコレータは渡されたfuncに.callsを追加します)私は2番目の(良い)バージョンが内側の関数の内部にバインドされているのに対して、func属性がバインドされているクロージャを作成しようとする悪いバージョンを理解していますが、 "悪い"バージョンは、 「良い」バージョンと同じ結果を得ることができます。decorator to track number関数呼び出し - 動作していない

def count_calls_bad(func): 
     func.calls = 0 
     def inner(*args,**kwargs): 
      func.calls += 1 #each call to inner increments func.calls (recur_n.calls) 
      return func(*args,**kwargs) 
     return inner 

    def count_calls_good(func): 
    def inner(*args, **kwargs): 
     inner.calls += 1 
     return func(*args, **kwargs) 
    inner.calls = 0 
    return inner 

    @count_calls_bad 
    def recur_n(num): 
     if num == 0: 
      return 0 
     print (num) 
     return recur_n(num-1) 

    recur_n(10) 
    print(recur_n.calls) #recur_n.calls attribute not bound any longer 

UPDATE:固定コードは、エディタでテストした後、関数名を更新するのを忘れていました。今recur_nが呼び出され、recur_10は呼び出されません。


また、私が遊んと問題がrecur_nがinnerになっていると思いますし、その最後の行print(recur_n.calls)は本当にprint(function count_calls_bad.<locals>.inner at 0x000000000364E2F0>)で、コールがrecur_n実際装飾されていない上に結合されて以来、そのオブジェクトは、何の属性callsを持っていませんでした。

あなたが実際にオリジナルの装飾のない関数にあなたの方法を強制すると、次の牛車で正しく更新された属性を取得することができます:

print(recur_n.__closure__[0].cell_contents.calls)

私の次の思考は、元の装飾のないを維持するためにfunctoolsの@wrapsを使用した後でしたこれは基本的に私が上でやっていることであり、デコレータに入り、装飾されていない名前のcall属性を引き出します。

from functools import wraps 
     def count_calls_bad(func): 
      func.calls = 0 
      @wraps func 
      def inner(*args,**kwargs): 
       func.calls += 1 #each call to inner increments func.calls (recur_n.calls) 
       return func(*args,**kwargs) 
      return inner 

これは少なくとも私に結果が得られますが、その結果はゼロです。だから、私は自分のオリジナルの質問に答えましたが、私は新しいもので終わります。なぜなら、@wrapsが関数を更新してrecur_nが内部ではなくrecur_nを参照するようになったから、11ではなく0が得られるのですか?

@wrapsは関数のシグネチャをコピーしますが、変数や属性などの他のデータを参照またはコピーすることはできません。

答えて

0

モジュールのトップレベルのrecur_nという名前が、デコレータから返されたラッパー関数innerを参照しているため、数がわからない理由があります。オリジナルのrecur_n関数を参照していません(ただし、ラッパー関数の__closure__属性を介してその関数にアクセスできます)。

functools.wrapsを使用しても、基本的な問題は変わりません。元の関数の属性の一部をラッパー関数にコピーするだけです。したがって、recur_nの機能は、__name__"inner"ではなく"recur_n"に設定され、__doc__は元のrecur_nのdocstring(一致する場合)と一致します。 wrapsコールは、関数の__dict__の属性の現在の値を新しいラッパー関数にコピーします。

関数の__name__を設定しても、モジュール名前空間で名前が参照する内容は変更されません。実際にwrapsを使用した後、両方の関数(元のrecur_nとラッパー関数)は同じ__name__属性、"recur_n"を持ちます。モジュールには、recur_nという名前で参照されているものが1つしかなく、どちらかまたは全く異なるものである可能性があります。あなたはデコレータでwrapsを使用しているときにrecur_n.callsをチェックするときにゼロが元の関数の__dict__の一部としてコピーされてしまったので、あなたが0を参照してください理由については

は、それはです。コピーは一度しか起こらなかったので(そして、Pythonの整数は不変なので、それらを更新することはできません)、インクリメントされたカウントは見ることができません。

1

少なくとも投稿コードにはrecur_nは定義されていません。あなたはrecur_10にデコレータを適用しました。

関連する問題