翻譯:《實用的Python編程》07_04_Function_decorators

目錄 | 上一節 (7.3 返回函數) | 下一節 (7.5 裝飾方法)python

7.4 函數裝飾器

本節介紹裝飾器(decorator)。由於這是一個高級主題,因此咱們只作簡單介紹。git

譯註:根據譯者我的的猜想,在《設計模式》(《 Design Patterns: Elements of Reusable Object-Oriented Software》)一書中寫到 decorator also known as wrapper,因此下文提到包裝器(wrapper),其實說的就是裝飾器。這裏爲了保持和原文一致,因此翻譯的時候沒有將「包裝器」替換爲「裝飾器」。github

日誌示例

考慮這樣一個函數:segmentfault

def add(x, y):
    return x + y

考慮給 add(x, y) 函數添加日誌功能:設計模式

def add(x, y):
    print('Calling add')
    return x + y

也帶有日誌功能的 sub(x, y)函數:app

def sub(x, y):
    print('Calling sub')
    return x - y

觀察

觀察: 這是一種重複。函數

在有大量重複代碼的地方編寫程序一般很煩人。這些代碼不只寫起來枯燥,維護起來也很麻煩。尤爲是你決定更改其工做方式的時候(例如,多是另外一種類型的日誌記錄)。工具

記錄日誌的代碼

也許你能夠建立一個添加了日誌功能的函數。例如包裝器(wrapper):性能

def logged(func):
    def wrapper(*args, **kwargs):
        print('Calling', func.__name__)
        return func(*args, **kwargs)
    return wrapper

使用該函數:this

def add(x, y):
    return x + y

logged_add = logged(add)

當調用 logged 返回的函數時會發生什麼?

logged_add(3, 4)      # You see the logging message appear

此示例闡明瞭建立所謂的包裝器函數(wrapper function) 的過程。

包裝器是一個函數,它包裝了另外一個帶有額外處理功能的函數,但在其它方面與原始函數的工做方式徹底相同。

>>> logged_add(3, 4)
Calling add   # Extra output. Added by the wrapper
7
>>>

注意事項:logged() 函數建立了一個包裝器,並做爲結果返回。

裝飾器

在 Python 中,在函數中使用包裝器很是常見。由於如此廣泛,因此有一個特殊的語法。

def add(x, y):
    return x + y
add = logged(add)

# Special syntax
@logged
def add(x, y):
    return x + y

該特殊語法執行與上面徹底相同的確切步驟。裝飾器只是一種新語法,用於裝飾函數。

說明

對於裝飾器而言,還有許多比這裏展現的更微妙的細節,例如,在類裏面使用裝飾器,或者對同一個函數使用多個裝飾器。不過,這裏的例子已經很好地說明了如何使用它們。通常而言,它是對出如今各類函數定義中的重複代碼的響應。裝飾器能夠將重複代碼移至中心定義。

練習

練習 7.10:計時裝飾器

若是你定義了一個函數,那麼函數的名稱和函數所屬模塊的名稱會分別存儲到 __name____module__屬性中。示例:

>>> def add(x,y):
     return x+y

>>> add.__name__
'add'
>>> add.__module__
'__main__'
>>>

請建立 timethis.py 文件,並在文件中編寫 timethis(func) 函數。timethis(func) 函數包裝一個具備額外邏輯層的函數,邏輯層打印出函數執行所須要的事件。爲此,你將在函數中添加以下計時調用。

start = time.time()
r = func(*args,**kwargs)
end = time.time()
print('%s.%s: %f' % (func.__module__, func.__name__, end-start))

timethis(func))裝飾器工做方式示例:

>>> from timethis import timethis
>>> @timethis
def countdown(n):
        while n > 0:
             n -= 1

>>> countdown(10000000)
__main__.countdown : 0.076562
>>>

討論:@timethis 裝飾器能夠放在任何函數的前面,即你應該把裝飾器用做性能調優(performance tuning)的診斷工具。

目錄 | 上一節 (7.3 返回函數) | 下一節 (7.5 裝飾方法)

注:完整翻譯見 https://github.com/codists/practical-python-zh

相關文章
相關標籤/搜索