2016-07-07 3 views
0

例: memoizeデコレータを使用したフィボナッチ再帰関数。関数ヘルパーを呼び出すときに引数はありません。関数ヘルパーが引数xを取るように定義されている場合、私は1つの引数で関数を呼び出すことを期待しています。私はそれがなぜ構文であるのか理解したいと思いますか?デコレータで関数を返すときに引数がないのはなぜですか?

def memoize(f): 
    memo = {} 
    def helper(x): 
     if x not in memo:    
      memo[x] = f(x) 
     return memo[x] 
    return helper 

@memoize 
def fib(n): 
    if n == 0: 
     return 0 
    elif n == 1: 
     return 1 
    else: 
     return fib(n-1) + fib(n-2) 

print(fib(40)) 

答えて

2

引数を指定してヘルパーを呼び出します。デコレータは、だからあなたのfib機能は、もはや元fib -functionあるこの

def fib(n): 
    if n == 0: 
     return 0 
    elif n == 1: 
     return 1 
    else: 
     return fib(n-1) + fib(n-2) 


fib = memoize(fib) 

ための糖衣構文ではありません。実際にはhelperクロージャーです。なぜなら、それはmemoizeが返すものですから、クロージャーです。だから、fib(40)に電話するとhelper(40)と呼びます。 memoizeデコレータは関数オブジェクトを作成し、それを呼び出さず、ただそれを返します。

0

文法はこのように、できるだけ合理的に見えると思います。 @(1つの引数をとり関数を返す関数)の後にデコレータオブジェクトを置くと、pythonは定義している関数でそれを呼び出します。この

@memoize 
def fib(n): 
    ... 

魔法デコレータ構文を使用しない、以下のとまったく同じです:

def fib(n): 
    ... 

fib = memoize(fib) 

あなたの頭が少しスピンしたい場合は、@が実際に続くことができることを考えます関数呼び出し - しかし、この関数呼び出しは上記のように動作するデコレータを返さなければなりません!装飾された関数が何度呼び出されたかを数える愚かな例ですが、開始値を設定することができます。 (それはちょうど例です:唯一の機能は、などを装飾することができるので、それは非常に便利ではありません)

def countcalls(start): 
    global _calls 
    _calls = start 
    def decorator(f): 
     def wrapper(x): 
      global _calls 
      _calls += 1 
      return f(x) 
     return wrapper 
    return decorator 

@countcalls(3) 
def say(s): 
    print(s) 

say("hello") 
# _calls is now 4 

ここで、装飾された機能をラップします(それを呼び出さず)countcalls(4)定義し、リターン機能decorator、および私が書いた関数の代わりにラッパーを返します。

関連する問題