2017-07-07 2 views
1

私はまだ得られなかったいくつかのpythonの機能について私の記憶をリフレッシュしています。私はthis python tutorialから学んでいます、そして、私が完全に理解していない例があります。これは、関数の呼び出しを数えるデコレータについてです、ここのコードです:Pythonデコレータは関数呼び出しをカウントします

def call_counter(func): 
    def helper(x): 
     helper.calls += 1 
     return func(x) 
    helper.calls = 0 
    return helper 

@call_counter 
def succ(x): 
    return x + 1 

if __name__ == '__main__': 
    print(succ.calls) 
    for i in range(10): 
     print(succ(i)) 
    print(succ.calls) 

私はここで取得しないのはなぜ私たちは関数ラッパーの呼び出しをインクリメントされて何(helper.calls + = 1)の代わりに、関数自体を呼び出す、なぜそれが実際に動作していますか?

+0

'@call_counter succ' ==' SUCC = call_counter(SUCC) =ヘルパー – Cheney

答えて

1

をデコレータについて覚えておくべき重要なことは、デコレータが機能であるということですその関数は引数として関数を受け取り、さらに別の関数を返します。戻り値 - さらに別の関数 - は、元の関数の名前が呼び出されたときに呼び出されるものです。

このモデルは非常に単純であることができる:この場合

def my_decorator(fn): 
    print("Decorator was called") 
    return fn 

は、返された関数は、渡された関数と同じです。しかし、それはあなたがしていることではありません。通常、完全に異なる関数を返すか、元の関数を何らかの形で連鎖またはラップする関数を返します。

def helper(x): 
    helper.calls += 1 
    return func(x) 

この内部関数は元の関数(return func(x))を呼び出しますが、それはまた、通話カウンタをインクリメント:非常に一般的なモデルであるあなたの例では、

は、あなたが返され、内部の機能を持っています。

この内部関数は、どのような関数が装飾されている場合でも「置換」として挿入されています。だから、モジュールfoo.succ()の関数が参照されると、結果はデコレータから返された内部ヘルパー関数への参照になります。この関数は呼び出しカウンタをインクリメントして、最初に定義されたsucc関数を呼び出します。

1

ここでは、関数呼び出しの代わりに関数ラッパー(helper.calls + = 1)の呼び出しをインクリメントして、なぜ実際には機能するのですか。

一般的に有用なデコレータにすると思います。あなたはうまく動作しますが、あなたはあまりにもこれを適用したいすべての機能に.calls +=1を入れて、あなたがそれらのいずれかを実行する前に0に初期化する必要があります。この

def succ(x): 
    succ.calls += 1 
    return x + 1 

if __name__ == '__main__': 
    succ.calls = 0 
    print(succ.calls) 
    for i in range(10): 
     print(succ(i)) 
    print(succ.calls) 

を行うことができます。あなたが数えたかった機能がたくさんある場合は、間違いなく良いです。それはプラスで0に初期化されます。

私はそれを理解として、それは(それが機能を飾るたびに再定義される)デコレータのでsucc = helpersucc.calls = helper.calls内からhelper機能と機能succを置き換えるので、それが動作します。 (ただし、名前ヘルパーはデコレータの名前空間内でのみ定義されます)

それは意味がありますか?

1

私は(私が間違っているなら、私を修正)あなたのプログラムが実行される順序は、このことを理解したよう:

  1. 登録call_function
  2. 登録succ
  3. succを登録しているときに、function interpreterがデコレータを見つけてcall_functionを実行します。
  4. 関数はオブジェクト(helper)を返します。このオブジェクト項目callsに追加します。
  5. succhelperに割り当てられました。したがって、関数を呼び出すと、実際にはhelperという関数がデコレータ内にラップされています。したがって、ヘルパー関数に追加するすべてのフィールドは、succという2つの変数が同じものを参照するため、外部からアクセスできます。
  6. あなたはsucc()呼び出すときにhelper(*args, **argv)

を行うかどうだから、基本的に同じだ、これをチェックアウト:

def helper(x): 
    helper.calls += 1 
    return 2 
helper.calls = 0 

def call_counter(func): 
    return helper 

@call_counter 
def succ(x): 
    return x + 1 

if __name__ == '__main__': 
    print(succ == helper) # prints true. 
1

"代用する"関数をデコレートすると、関数はラッパーで機能します。

この例では、装飾の後にsuccを呼び出すと、実際にはhelperが呼び出されます。コールをカウントしている場合は、helperコールを増やす必要があります。

あなたが装飾された機能の__name__属性をチェックすることによって、あなたは機能を飾る一度名前がラッパーカントーバインドされていることを確認することができます

def call_counter(func): 
    def helper(x): 
     helper.calls += 1 
     return func(x) 
    helper.calls = 0 
    return helper 

@call_counter 
def succ(x): 
    return x + 1 

print(succ.__name__) 
>>> 'helper' 
関連する問題