9.2 創(chuàng)建裝飾器時(shí)保留函數(shù)元信息

2018-02-24 15:27 更新

問(wèn)題

你寫了一個(gè)裝飾器作用在某個(gè)函數(shù)上,但是這個(gè)函數(shù)的重要的元信息比如名字、文檔字符串、注解和參數(shù)簽名都丟失了。

解決方案

任何時(shí)候你定義裝飾器的時(shí)候,都應(yīng)該使用 functools 庫(kù)中的 @wraps 裝飾器來(lái)注解底層包裝函數(shù)。例如:

import time
from functools import wraps
def timethis(func):
    '''
    Decorator that reports the execution time.
    '''
    @wraps(func)
    def wrapper(*args, **kwargs):
        start = time.time()
        result = func(*args, **kwargs)
        end = time.time()
        print(func.__name__, end-start)
        return result
    return wrapper

下面我們使用這個(gè)被包裝后的函數(shù)并檢查它的元信息:

>>> @timethis
... def countdown(n:int):
...     '''
...     Counts down
...     '''
...     while n > 0:
...         n -= 1
...
>>> countdown(100000)
countdown 0.008917808532714844
>>> countdown.__name__
'countdown'
>>> countdown.__doc__
'\n\tCounts down\n\t'
>>> countdown.__annotations__
{'n': <class 'int'>}
>>>

討論

在編寫裝飾器的時(shí)候復(fù)制元信息是一個(gè)非常重要的部分。如果你忘記了使用 @wrap ,那么你會(huì)發(fā)現(xiàn)被裝飾函數(shù)丟失了所有有用的信息。比如如果忽略 @wrap 后的效果是下面這樣的:

>>> countdown.__name__
'wrapper'
>>> countdown.__doc__
>>> countdown.__annotations__
{}
>>>

@wraps 有一個(gè)重要特征是它能讓你通過(guò)屬性 __wrapped__ 直接訪問(wèn)被包裝函數(shù)。例如:

>>> countdown.__wrapped__(100000)
>>>

__wrapped__ 屬性還能讓被裝飾函數(shù)正確暴露底層的參數(shù)簽名信息。例如:

>>> from inspect import signature
>>> print(signature(countdown))
(n:int)
>>>

一個(gè)很普遍的問(wèn)題是怎樣讓裝飾器去直接復(fù)制原始函數(shù)的參數(shù)簽名信息,如果想自己手動(dòng)實(shí)現(xiàn)的話需要做大量的工作,最好就簡(jiǎn)單的使用 __wrapped__ 裝飾器。通過(guò)底層的 __wrapped__ 屬性訪問(wèn)到函數(shù)簽名信息。更多關(guān)于簽名的內(nèi)容可以參考9.16小節(jié)。

以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)