2016-04-05 145 views
5

我從thecodeship上的一個很棒的教程中學到了一些關於裝飾器的知識,但是通過一個示例發現自己頗爲困惑。Python裝飾器示例

首先給出一個簡單的例子,然後給出一個裝飾器的解釋。

def p_decorate(func): 
    def func_wrapper(name): 
     return "<p>{0}</p>".format(func(name)) 
    return func_wrapper 

def get_text(name): 
    return "lorem ipsum, {0} dolor sit amet".format(name) 

my_get_text = p_decorate(get_text) 

print my_get_text("John") 

現在這對我有意義。裝飾器只是一個函數的包裝器。在這個傢伙的解釋中,他說裝飾器是一個函數,它將另一個函數作爲參數,生成一個新函數,並返回生成的函數以在任何地方使用。

現在相當於上面的代碼是:

def p_decorate(func): 
    def func_wrapper(name): 
     return "<p>{0}</p>".format(func(name)) 
    return func_wrapper 

@p_decorate 
def get_text(name): 
    return "lorem ipsum, {0} dolor sit amet".format(name) 

print get_text("John") 

我相信我明白如果沒有參數,一個裝飾初始化的方式。糾正我,如果我錯了。

  • 默認情況下,經過裝飾的功能get_text因爲p_decorate返回一個函數func_wrapper,我們最終與真實的陳述get_text = func_wrapper

對我來說重要的是第一個代碼塊的等價物,因爲我看到並理解裝飾器的行爲。

什麼很讓我困惑的是下面的代碼:

def tags(tag_name): 
    def tags_decorator(func): 
     def func_wrapper(name): 
      return "<{0}>{1}</{0}>".format(tag_name, func(name)) 
     return func_wrapper 
    return tags_decorator 

@tags("p") 
def get_text(name): 
    return "Hello "+name 

print get_text("John") 

再次,糾正我,如果我錯了,但是這是我的理解。

  • 裝飾器接受標記字符串「p」而不是 默認函數名稱。然後函數tags_decorator 假定將要傳遞的參數是裝飾的函數 ,get_text

對於我來說,在「非裝飾器」形式中看到等效的代碼塊可能會有所幫助,但我似乎無法將我的頭圍繞在看起來像什麼樣的東西上。我也不理解爲什麼tags_decoratorfunc_wrapper都返回。如果裝飾者只需要返回1個函數來包裝get_text,那麼返回兩個不同函數的目的是什麼。

作爲一個方面說明,它真的歸結爲以下幾點。

  • 該塊可以簡化爲少於一組3個功能嗎?
  • 裝飾器可以接受多於一個參數來簡化代碼嗎?

回答

7

在一定範圍內,在@後一切都執行農產品一個裝飾。在你的第一個例子,接下來後@只是一個名字:

@p_decorate 

所以Python查找p_decorate並與裝飾功能作爲參數調用它:

get_text = p_decorate(get_text) 

(過於簡單化了一點, get_text最初沒有綁定到原始函數,但你已經獲得了要點)。

在第二個例子中,裝飾表達式是一個涉及多一點,它包括呼叫:

@tags("p") 

所以Python使用tags("p")找到裝飾。這到底是則執行什麼:

get_text = tags("p")(get_text) 

輸出的tags("p")是這裏的裝飾!我把tags函數本身稱爲裝飾器工廠,它在調用時會產生一個裝飾器。當您撥打tags()時,它將返回tags_decorator()。這是真實裝飾這裏。

你可以刪除,而不是裝飾功能和硬編碼"p"值,直接使用:

def tags_decorator_p(func): 
    def func_wrapper(name): 
     return "<{0}>{1}</{0}>".format("p", func(name)) 
    return func_wrapper 

@tags_decorator_p 
def get_text(name): 
    # ... 

但你不得不爲的說法,以tags()每個可能值創建單獨的裝飾。這是一個裝飾工廠的價值,你可以爲裝飾添加參數並改變你的裝飾功能。

裝飾者工廠可以採取任何數量的參數;它只是您調用來生成裝飾器的函數。修飾器本身只能接受一個參數,函數來裝飾。

我說,範圍內在我的答案開始的原因; @後面的表達式的語法只允許虛線名稱(foo.bar.baz,屬性訪問)和調用((arg1, arg2, keyword=arg3))。請參閱reference documentation。原始PEP 318陳述爲:

裝飾器語句在它可以接受的範圍內受到限制 - 任意表達式不起作用。 Guido喜歡這個,因爲有一種直覺[17]。

+0

謝謝。我在回覆後做了一個簡短的編輯。爲什麼這些函數返回2個函數而不是1? – Max

+1

@Max:看我最近的編輯。一個是裝飾器(由裝飾工廠生產),另一個是用包裝代替原裝函數,裝飾器的結果。 –

+0

我現在明白了,2個函數調用的目的。我可以用裝飾工廠製作一個特定的裝飾器來進一步指定我的最終包裝。 – Max