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