python中的裝飾器裝飾過的函數其實就不是函數自己了,咱們能夠看看下面的例子html
import time def timmer(func): """timmer doc""" def inner(*args, **kwargs): """inner doc""" start = time.time() res = func(*args, **kwargs) end = time.time() print("函數運行時間爲 %s" % (end - start)) return res return inner @timmer def func_test(): """func_test doc""" time.sleep(2) return print(func_test.__name__) # inner print(func_test.__doc__) # inner doc
按咱們正常的思惟,func_test.__name__應該拿到的就是「func_test」,因此這個結果就印證了上面的第一句話,可是這是咱們加一個@wraps,就會發現好像一切都正常了:python
import time from functools import wraps def timmer(func): """timmer doc""" @wraps(func) def inner(*args, **kwargs): """inner doc""" start = time.time() res = func(*args, **kwargs) end = time.time() print("函數運行時間爲 %s" % (end - start)) return res return inner @timmer def func_test(): """func_test doc""" time.sleep(2) return print(func_test.__name__) # func_test print(func_test.__doc__) # func_test doc
爲了方便理解,我把源碼和例子放在了一塊兒,這樣的話咱們看着會方便:app
import time from functools import partial WRAPPER_ASSIGNMENTS = ('__module__', '__name__', '__qualname__', '__doc__', '__annotations__') WRAPPER_UPDATES = ('__dict__',) def update_wrapper(wrapper, # inner wrapped, # func_test assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES): """Update a wrapper function to look like the wrapped function wrapper is the function to be updated wrapped is the original function assigned is a tuple naming the attributes assigned directly from the wrapped function to the wrapper function (defaults to functools.WRAPPER_ASSIGNMENTS) updated is a tuple naming the attributes of the wrapper that are updated with the corresponding attribute from the wrapped function (defaults to functools.WRAPPER_UPDATES) """ print('update_wrapper 執行...') for attr in assigned: try: value = getattr(wrapped, attr) except AttributeError: pass else: setattr(wrapper, attr, value) for attr in updated: getattr(wrapper, attr).update(getattr(wrapped, attr, {})) # Issue #17482: set __wrapped__ last so we don't inadvertently copy it # from the wrapped function when updating __dict__ wrapper.__wrapped__ = wrapped # Return the wrapper so this can be used as a decorator via partial() print('update_wrapper 執行結束') return wrapper def wraps(wrapped, assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES): """Decorator factory to apply update_wrapper() to a wrapper function Returns a decorator that invokes update_wrapper() with the decorated function as the wrapper argument and the arguments to wraps() as the remaining arguments. Default arguments are as for update_wrapper(). This is a convenience function to simplify applying partial() to update_wrapper(). """ print('wraps 執行...') print('wraps 執行結束') # 純粹爲了打印出來的結果好理解 return partial(update_wrapper, wrapped=wrapped, assigned=assigned, updated=updated) def timmer(func): print('timmer 執行...') @wraps(func) # inner = update_wrapper的返回值 def inner(*args, **kwargs): start = time.time() res = func(*args, **kwargs) end = time.time() print("函數運行時間爲 %s" % (end - start)) return res print('timmer 執行結束') # 固然不是真正的結束,執行完下一行才結束 return inner @timmer def func_test(): print("func_test 執行...") time.sleep(2) print("func_test 運行結束") return func_test() """ 打印結果以下: timmer 執行... wraps 執行... wraps 執行結束 update_wrapper 執行... update_wrapper 執行結束 timmer 執行結束 func_test 執行... func_test 運行結束 函數運行時間爲 2.0000197887420654 從打印的結果咱們能夠看出,@語法會在函數定義或者說模塊初始化階段(可能稱呼不對,之後回來改)就執行了 """
上面的例子中我加了不少打印,主要是爲了提醒一下在func_test()函數執行以前,@語法已經執行了。函數
其實原理很簡單,用了一個偏函數,去執行update_wrapper,真正起做用的也是這個函數,func_test執行以前,update_wrapper函數就會把inner函數的好多屬性(示例中WRAPPER_ASSIGNMENTS,WRAPPER_UPDATES指向的屬性 ,還有__wrapped__屬性)所有其換成func_test的屬性。this