1. 程式人生 > >python 裝飾器 Decorator

python 裝飾器 Decorator

log 運行 效果 args 增加 原來 函數 app per

一、裝飾器定義

在代碼運行期間動態增加功能的方式,稱之為“裝飾器”(Decorator)。本質上,Decorator就是一個返回函數的高階函數。

 1 >>> def log(func):
 2 ...     def wrapper(*args, **kw):
 3 ...         print(call %s: % func.__name__)
 4 ...         return func(*args, **kw)
 5 ...     return wrapper
 6 ... 
 7 >>> @log
 8 ... def now():
9 ... print(2017-12-16) 10 ... 11 >>> now() 12 call now: 13 2017-12-16 14 >>>

觀察上面的log,因為它是一個decorator,所以接受一個函數作為參數,並返回一個函數。要借助Python的@語法,把decorator置於函數的定義處。

@log放到now()函數的定義處,相當於執行了語句:

1 >>>now = log(now)

二、帶傳參的裝飾器

 1 >>> def log(text):
 2 ...     def decorator(func):
3 ... def wrapper(*args, **kw): 4 ... print(%s %s: % (text, func.__name__)) 5 ... return func(*args, **kw) 6 ... return wrapper 7 ... return decorator 8 ... 9 >>> @log(execute) 10 ... def now(): 11 ... print(2017-12-16) 12 ... 13 >>> now()
14 execute now: 15 2017-12-16

和兩層嵌套的decorator相比,3層嵌套的效果是這樣的:

1 >>> now = log(execute)(now)

三、functools.wraps

以上兩種decorator的定義都沒有問題,但還差最後一步。因為我們講了函數也是對象,它有__name__等屬性,但你去看經過decorator裝飾之後的函數,它們的__name__已經從原來的‘now‘變成了‘wrapper‘

1 >>> now.__name__
2 wrapper

因為返回的那個wrapper()函數名字就是‘wrapper‘,所以,需要把原始函數的__name__等屬性復制到wrapper()函數中,否則,有些依賴函數簽名的代碼執行就會出錯。

不需要編寫wrapper.__name__ = func.__name__這樣的代碼,Python內置的functools.wraps就是幹這個事的,所以,一個完整的decorator的寫法如下:

1 import functools
2 
3 def log(func):
4     @functools.wraps(func)
5     def wrapper(*args, **kw):
6         print(call %s(): % func.__name__)
7         return func(*args, **kw)
8     return wrapper
 1 import functools
 2 
 3 def log(text):
 4     def decorator(func):
 5         @functools.wraps(func)
 6         def wrapper(*args, **kw):
 7             print(%s %s(): % (text, func.__name__))
 8             return func(*args, **kw)
 9         return wrapper
10     return decorator

python 裝飾器 Decorator