在了解
wraps
修飾器之前,我們首先要了解
partial
和
update_wrapper
這兩個(gè)函數(shù),因?yàn)樵?/span>
wraps
的代碼中,用到了這兩個(gè)函數(shù)。
partial
首先說
partial
函數(shù),在
官方文檔
的描述中,這個(gè)函數(shù)的聲明如下:
functools.partial(func, *args, **keywords)
。它的作用就是返回一個(gè)
partial
對象,當(dāng)這個(gè)
partial
對象被調(diào)用的時(shí)候,就像通過
func(*args, **kwargs)
的形式來調(diào)用
func
函數(shù)一樣。如果有額外的
位置參數(shù)( args)
或者
關(guān)鍵字參數(shù)(*kwargs)
被傳給了這個(gè)
partial
對象,那它們也都會被傳遞給
func
函數(shù),如果一個(gè)參數(shù)被多次傳入,那么后面的值會覆蓋前面的值。
個(gè)人感覺這個(gè)函數(shù)很像C++
中的
bind
函數(shù),都是把某個(gè)函數(shù)的某個(gè)參數(shù)固定,從而構(gòu)造出一個(gè)新的函數(shù)來。比如下面這個(gè)例子:
from functools
import partial
def
add(x:int, y:int):
return x+y
#
這里創(chuàng)造了一個(gè)新的函數(shù)
add2
,只接受一個(gè)整型參數(shù),然后將這個(gè)參數(shù)統(tǒng)一加上
2
add2 = partial(add, y=2)
add2(3) #
這里將會輸出
5
這個(gè)函數(shù)是使用C
而不是
Python
實(shí)現(xiàn)的,但是官方文檔中給出了
Python
實(shí)現(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 newfunc
update_wrapper
接下來,我們再來聊一聊
update_wrapper
這個(gè)函數(shù),顧名思義,這個(gè)函數(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),這個(gè)函數(shù)的作用就是從
被修飾的函數(shù)(wrapped)
中取出一些屬性值來,賦值給
修飾器函數(shù)(wrapper)
。為什么要這么做呢,我們看下面這個(gè)例子。
自定義修飾器v1
首先我們寫個(gè)自定義的修飾器,沒有任何的功能,僅有文檔字符串,如下所示:
def
wrapper(f):
def
wrapper_function(*args, **kwargs):
"""
這個(gè)是修飾函數(shù)
"""
return f(*args, **kwargs)
return wrapper_function
@wrapper
def
wrapped():
"""
這個(gè)是被修飾的函數(shù)
"""
print('wrapped')
print(wrapped.__doc__) #
輸出
`
這個(gè)是修飾函數(shù)
`
print(wrapped.__name__) #
輸出
`wrapper_function`
從上面的例子我們可以看到,我想要獲取
wrapped
這個(gè)被修飾函數(shù)的文檔字符串,但是卻獲取成了
wrapper_function
的文檔字符串,
wrapped
函數(shù)的名字也變成了
wrapper_function
函數(shù)的名字。這是因?yàn)榻o
wrapped
添加上
@wrapper
修飾器相當(dāng)于執(zhí)行了一句
wrapped = wrapper(wrapped)
,執(zhí)行完這條語句之后,
wrapped
函數(shù)就變成了
wrapper_function
函數(shù)。遇到這種情況該怎么辦呢,首先我們可以手動地在
wrapper
函數(shù)中更改
wrapper_function
的
__doc__
和
__name__
屬性,但聰明的你肯定也想到了,我們可以直接用
update_wrapper
函數(shù)來實(shí)現(xiàn)這個(gè)功能。
自定義修飾器v2
我們對上面定義的修飾器稍作修改,添加了一句
update_wrapper(wrapper_function, f)
。
from functools
import update_wrapper
def
wrapper(f):
def
wrapper_function(*args, **kwargs):
"""
這個(gè)是修飾函數(shù)
"""
return f(*args, **kwargs)
update_wrapper(wrapper_function, f) # <<
添加了這條語句
return wrapper_function
@wrapper
def
wrapped():
"""
這個(gè)是被修飾的函數(shù)
"""
print('wrapped')
print(wrapped.__doc__) #
輸出
`
這個(gè)是被修飾的函數(shù)
`
print(wrapped.__name__) #
輸出
`wrapped`
此時(shí)我們可以發(fā)現(xiàn),
__doc__
和
__name__
屬性已經(jīng)能夠按我們預(yù)想的那樣顯示了,除此之外,
update_wrapper
函數(shù)也對
__module__
和
__dict__
等屬性進(jìn)行了更改和更新。
wraps修飾器
OK
,至此,我們已經(jīng)了解了
partial
和
update_wrapper
這兩個(gè)函數(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)
沒錯(cuò),就是這么的簡單,只有這么一句,我們可以看出,
wraps
函數(shù)其實(shí)就是一個(gè)修飾器版的
update_wrapper
函數(shù),它的功能和
update_wrapper
是一模一樣的。我們可以修改我們上面的自定義修飾器的例子,做出一個(gè)更方便閱讀的版本。
自定義修飾器v3
from functools
import wraps
def
wrapper(f): @wraps(f)
def
wrapper_function(*args, **kwargs):
"""
這個(gè)是修飾函數(shù)
"""
return f(*args, **kwargs)
return wrapper_function
@wrapper
def
wrapped():
"""
這個(gè)是被修飾的函數(shù)
"""
print('wrapped')
print(wrapped.__doc__) #
輸出
`
這個(gè)是被修飾的函數(shù)
`
print(wrapped.__name__) #
輸出
`wrapped`
至此,我想大家應(yīng)該明白
wraps
這個(gè)修飾器的作用了吧,就是將
被修飾的函數(shù)(wrapped)
的一些屬性值賦值給
修飾器函數(shù)(wrapper)
,最終讓屬性的顯示更符合我們的直覺。
來源:SegmentFault