python 裝飾器 Decorator

1、裝飾器定義app

在代碼運行期間動態增長功能的方式,稱之爲「裝飾器」(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置於函數的定義處。spa

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

1 >>>now = log(now)

2、帶傳參的裝飾器對象

 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層嵌套的效果是這樣的:blog

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

3、functools.wrapsclass

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

1 >>> now.__name__
2 'wrapper'

由於返回的那個wrapper()函數名字就是'wrapper',因此,須要把原始函數的__name__等屬性複製到wrapper()函數中,不然,有些依賴函數簽名的代碼執行就會出錯。語法

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

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
相關文章
相關標籤/搜索