在函數對象中保存着一些函數的元數據,例如:
__name__ 函數的名字
__doc__ 函數文檔字符串
__module__ 函數所屬模塊名
__dict__ 屬性字典
__default__ 默認參數元組
...
在使用裝飾器後,再訪問上面這些屬性時,看到的是內部包裹函數的元數據,原來函數的元數據便丟失了。
要求:爲被裝飾函數保存元數據。
解決方案:
-
使用標準庫functools中的
update_wrapper()
函數更新內部包裹函數的屬性爲被包裹函數的屬性。 -
使用標準庫functools中的
wraps
裝飾內部包裹函數,可以指定將原函數的某些屬性,更新到包裹函數。
- 對於
functools.update_wrapper()
函數:
functools.update_wrapper(wrapper, wrapped, assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES)
此函數的主要用途是包裝修飾函數並返回wrapper函數。wrapper函數是包裹函數,wrapped函數就是被包裹函數。
更新wrapper函數,使其看起來像wrapped函數。可選參數assigned是元組,用於指定將原始函數的哪些屬性直接分配給wrapper函數上匹配的屬性,以及使用原始函數的相應屬性更新wrapper函數的哪些屬性(比如__dict__
屬性字典)。
>>> from functools import WRAPPER_ASSIGNMENTS
>>> WRAPPER_ASSIGNMENTS
('__module__', '__name__', '__qualname__', '__doc__', '__annotations__')
>>> from functools import WRAPPER_UPDATES
>>> WRAPPER_UPDATES
('__dict__',)
一般來說,使用默認參數即可,即functools.update_wrapper(wrapper, wrapped)
。
- 對於
functools.wraps()
函數:
functools.wraps(wrapped, assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES)
用於在定義wrapper函數(包裹函數)時調用update_wrapper()
作爲函數裝飾器。它等價於partial(update_wrapper, wrapped=wrapped, assigned=assigned, updated=updated)
。作爲wrapper函數的裝飾器時參數是wrapped函數名(被包裹函數名)。
- 方案1示例:
from functools import update_wrapper
def my_decorator(func):
def wrap(*args ,**kwargs):
'''某功能包裹函數'''
#此處實現某種功能
return func(*args ,**kwargs)
update_wrapper(wrap, func) #保留被裝飾函數元數據
return wrap
@my_decorator
def xxx_func(a, b):
'''xxx_func函數文檔:...'''
pass
print(xxx_func.__name__)
print(xxx_func.__doc__)
xxx_func #結果
xxx_func函數文檔:...
- 方案2示例:
from functools import wraps
def my_decorator(func):
@wraps(func) #保留被裝飾函數元數據
def wrap(*args ,**kwargs):
'''某功能包裹函數'''
#此處實現某種功能
return func(*args ,**kwargs)
return wrap
@my_decorator
def xxx_func(a, b):
'''xxx_func函數文檔:...'''
pass
print(xxx_func.__name__)
print(xxx_func.__doc__)
xxx_func #結果
xxx_func函數文檔:..
上面兩種方案都可以爲被裝飾函數保存元數據,方案2更爲簡便。