摘要:首先說函數,在官方文檔的描述中,這個函數的聲明如下。這是因為給添加上修飾器相當于執行了一句,執行完這條語句之后,函數就變成了函數。自定義修飾器我們對上面定義的修飾器稍作修改,添加了一句。參考鏈接裝飾器和模塊源碼
預備知識
在了解wraps修飾器之前,我們首先要了解partial和update_wrapper這兩個函數,因為在wraps的代碼中,用到了這兩個函數。
partial首先說partial函數,在官方文檔的描述中,這個函數的聲明如下:functools.partial(func, *args, **keywords)。它的作用就是返回一個partial對象,當這個partial對象被調用的時候,就像通過func(*args, **kwargs)的形式來調用func函數一樣。如果有額外的 位置參數(args) 或者 關鍵字參數(*kwargs) 被傳給了這個partial對象,那它們也都會被傳遞給func函數,如果一個參數被多次傳入,那么后面的值會覆蓋前面的值。
個人感覺這個函數很像C++中的bind函數,都是把某個函數的某個參數固定,從而構造出一個新的函數來。比如下面這個例子:
from functools import partial def add(x:int, y:int): return x+y # 這里創造了一個新的函數add2,只接受一個整型參數,然后將這個參數統一加上2 add2 = partial(add, y=2) add2(3) # 這里將會輸出5
這個函數是使用C而不是Python實現的,但是官方文檔中給出了Python實現的代碼,如下所示,大家可以進行參考:
def partial(func, *args, **keywords): def newfunc(*fargs, **fkeywords): newkeywords = keywords.copy() newkeywords.update(fkeywords) return func(*args, *fargs, **newkeywords) newfunc.func = func newfunc.args = args newfunc.keywords = keywords return newfuncupdate_wrapper
接下來,我們再來聊一聊update_wrapper這個函數,顧名思義,這個函數就是用來更新修飾器函數的,具體更新些什么呢,我們可以直接把它的源碼搬過來看一下:
WRAPPER_ASSIGNMENTS = ("__module__", "__name__", "__qualname__", "__doc__", "__annotations__") WRAPPER_UPDATES = ("__dict__",) def update_wrapper(wrapper, wrapped, assigned = WRAPPER_ASSIGNMENTS, updated = WRAPPER_UPDATES): 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, {})) wrapper.__wrapped__ = wrapped return wrapper
大家可以發現,這個函數的作用就是從 被修飾的函數(wrapped) 中取出一些屬性值來,賦值給 修飾器函數(wrapper) 。為什么要這么做呢,我們看下面這個例子。
自定義修飾器v1首先我們寫個自定義的修飾器,沒有任何的功能,僅有文檔字符串,如下所示:
def wrapper(f): def wrapper_function(*args, **kwargs): """這個是修飾函數""" return f(*args, **kwargs) return wrapper_function @wrapper def wrapped(): """這個是被修飾的函數""" print("wrapped") print(wrapped.__doc__) # 輸出`這個是修飾函數` print(wrapped.__name__) # 輸出`wrapper_function`
從上面的例子我們可以看到,我想要獲取wrapped這個被修飾函數的文檔字符串,但是卻獲取成了wrapper_function的文檔字符串,wrapped函數的名字也變成了wrapper_function函數的名字。這是因為給wrapped添加上@wrapper修飾器相當于執行了一句wrapped = wrapper(wrapped),執行完這條語句之后,wrapped函數就變成了wrapper_function函數。遇到這種情況該怎么辦呢,首先我們可以手動地在wrapper函數中更改wrapper_function的__doc__和__name__屬性,但聰明的你肯定也想到了,我們可以直接用update_wrapper函數來實現這個功能。
自定義修飾器v2我們對上面定義的修飾器稍作修改,添加了一句update_wrapper(wrapper_function, f)。
from functools import update_wrapper def wrapper(f): def wrapper_function(*args, **kwargs): """這個是修飾函數""" return f(*args, **kwargs) update_wrapper(wrapper_function, f) # << 添加了這條語句 return wrapper_function @wrapper def wrapped(): """這個是被修飾的函數""" print("wrapped") print(wrapped.__doc__) # 輸出`這個是被修飾的函數` print(wrapped.__name__) # 輸出`wrapped`
此時我們可以發現,__doc__和__name__屬性已經能夠按我們預想的那樣顯示了,除此之外,update_wrapper函數也對__module__和__dict__等屬性進行了更改和更新。
wraps修飾器OK,至此,我們已經了解了partial和update_wrapper這兩個函數的功能,接下來我們翻出wraps修飾器的源碼:
WRAPPER_ASSIGNMENTS = ("__module__", "__name__", "__qualname__", "__doc__", "__annotations__") WRAPPER_UPDATES = ("__dict__",) def wraps(wrapped, assigned = WRAPPER_ASSIGNMENTS, updated = WRAPPER_UPDATES): return partial(update_wrapper, wrapped=wrapped, assigned=assigned, updated=updated)
沒錯,就是這么的簡單,只有這么一句,我們可以看出,wraps函數其實就是一個修飾器版的update_wrapper函數,它的功能和update_wrapper是一模一樣的。我們可以修改我們上面的自定義修飾器的例子,做出一個更方便閱讀的版本。
自定義修飾器v3from functools import wraps def wrapper(f): @wraps(f) def wrapper_function(*args, **kwargs): """這個是修飾函數""" return f(*args, **kwargs) return wrapper_function @wrapper def wrapped(): """這個是被修飾的函數 """ print("wrapped") print(wrapped.__doc__) # 輸出`這個是被修飾的函數` print(wrapped.__name__) # 輸出`wrapped`
至此,我想大家應該明白wraps這個修飾器的作用了吧,就是將 被修飾的函數(wrapped) 的一些屬性值賦值給 修飾器函數(wrapper) ,最終讓屬性的顯示更符合我們的直覺。
參考鏈接python3 functools.wraps
python裝飾器和functools模塊
Github - cpython functools源碼
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/44369.html
摘要:然后煎魚加了一個后再調用函數,得到的輸出結果和加修飾器的一樣,換言之等效于因此,我們對于,可以理解是,它通過閉包的方式把新函數的引用賦值給了原來函數的引用。 Python有什么好學的這句話可不是反問句,而是問句哦。 主要是煎魚覺得太多的人覺得Python的語法較為簡單,寫出來的代碼只要符合邏輯,不需要太多的學習即可,即可從一門其他語言跳來用Python寫(當然這樣是好事,誰都希望入門簡...
項目地址:https://git.io/pytips Python 的修飾器是一種語法糖(Syntactic Sugar),也就是說: @decorator @wrap def func(): pass 是下面語法的一種簡寫: def func(): pass func = decorator(wrap(func)) 關于修飾器的兩個主要問題: 修飾器用來修飾誰 誰可以作為修飾器...
摘要:一般情況下,我們使用裝飾器提供的語法糖,來簡化上面的寫法像上面的情況,可以動態修改函數或類功能的函數就是裝飾器。本文標題為會打扮的裝飾器本文鏈接為參考資料修飾器的函數式編程中的裝飾器介紹思誠之道裝飾器入門與提高賴明星 裝飾器 我們知道,在 Python 中,我們可以像使用變量一樣使用函數: 函數可以被賦值給其他變量 函數可以被刪除 可以在函數里面再定義函數 函數可以作為參數傳遞給另外...
摘要:的裝飾器是用來裝飾函數的。簡單裝飾器裝飾器的語法糖是使用符號表示,裝飾器本身也是一個函數,只不過參數是函數而已。保留函數的元信息被修飾之后的函數,它的元信息都消失,被替換的函數代替。中提供了來保存函數的元信息。 python的裝飾器是用來裝飾函數的。這是什么意思呢?假如我們有一個函數,這個函數的功能不能滿足我們現有的需求,那么我們可以通過裝飾器在這個函數執行前執行后做一些我們需要的操作...
閱讀 2400·2021-09-08 09:45
閱讀 3340·2021-09-08 09:45
閱讀 3097·2019-08-30 15:54
閱讀 3348·2019-08-26 13:54
閱讀 1405·2019-08-26 13:26
閱讀 1384·2019-08-26 13:23
閱讀 909·2019-08-23 17:57
閱讀 2178·2019-08-23 17:14