2017-11-25 112 views
0

首先我知道裝飾者是什麼。我想了解一些小方面。python裝飾器是如何定義的?

TL; DR

is decorator func1 = func2(func1) # True 
is decorator func3 = func2(func1) # ??? 

讓我們來看看Wiki描述兩個等價的方法來裝飾功能:

@viking_chorus 
def menu_item(): 
    print("spam") 

def menu_item(): 
    print("spam") 
menu_item = viking_chorus(menu_item) 

現在,讓我們看看在這個website描述,特別是在部分回到裝飾者。定義了兩個功能:

def make_pretty(func): 
    def inner(): 
     print("I got decorated") 
     func() 
    return inner 

def ordinary(): 
    print("I am ordinary") 

然後作者裝飾功能,並調用它:

>>> pretty = make_pretty(ordinary) 
>>> pretty() 
I got decorated 
I am ordinary 

我們可以看到,筆者不使用:

>>> ordinary = make_pretty(ordinary) 

這是Wiki推薦的方式(我知道Wiki有時可能是錯誤的)。所以我決定用最後的方式來裝點從這個tutorial採取的斐波那契數函數:

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


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

這一呼籲:

>>> fib_element = memoize(fib) 
>>> fib_element(40) 

把我的機器上很長一段時間,這意味着fib()已沒有正確裝飾。執行時間與fib(40)相當。這些電話:

>>> fib = memoize(fib) 
>>> fib_element = fib # assigned after decoration 
>>> fib(40) 
>>> fib_element(40) 

執行得很快。所以問題是:我們可以說我們在pretty = make_pretty(ordinary)賦值中裝飾了ordinary函數嗎?

+1

從技術上講,沒有一個獨特的對象叫做裝飾器;它實際上只是一個將函數作爲參數並返回(或預期返回)另一個函數的函數。 * * *特別是decorator * syntax *,它提供了一個簡潔的方式來應用裝飾器函數。 '@foo def bar():...'比'def bar():...短; ...; bar = foo(bar)'。 – chepner

回答

2

第一次撥打fib_element(40)的原因很慢,原因是您不會遞歸裝飾:fib函數不知道您的記憶。如果您在同一元素上重複呼叫fib_element,則第一個呼叫將會很慢,其他呼叫則會非常快。

由於fib電話fib(這指的是在第二個例子中的裝飾功能,但在第一個例子中的原始功能),你需要給它的同名從記憶化與這種裝飾方法中受益。

+0

因此,如果在第一次調用期間''fib'函數不知道memoization,爲什麼第二個和下一個調用具有相同的參數(如您所述)使用字典和'fib_element(N)'快速工作?這表明'fib'知道memoization。 – Benek

+1

因爲'fib'現在是memoized'fib'。 – L3viathan

+0

實際上'fib_element = fib'只會使兩個名字指向相同的函數,它們並不是獨特的函數。 –