9.2 创建装饰器时�留函数元信�¶

问题¶

你写了一个装饰器作用在�个函数上,但是这个函数的��的元信�比如�字�文档字符串�注解和�数签�都丢失了。

解决方案¶

任何时候你定义装饰器的时候,都应该使用 functools 库中的 @wraps 装饰器�注解底层包装函数。例如:

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

下�我们使用这个被包装�的函数并检查它的元信�:

>>> @timethis
... def countdown(n):
...     '''
...     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'>}
>>>

讨论¶

在编写装饰器的时候�制元信�是一个�常��的部分。如果你忘记了使用 @wraps , 那么你会�现被装饰函数丢失了所有有用的信�。比如如果忽略 @wraps �的效果是下�这样的:

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

@wraps 有一个��特�是它能让你通过属性 __wrapped__ 直接访问被包装函数。例如:

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

__wrapped__ 属性还能让被装饰函数正确暴露底层的�数签�信�。例如:

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

一个很普�的问题是怎样让装饰器去直接�制原始函数的�数签�信�, 如果想自己手动实现的�需��大�的工作,最好就简�的使用 @wraps 装饰器。 通过底层的 __wrapped__ 属性访问到函数签�信�。更多关于签�的内容�以�考9.16�节。