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å°�节。