Python 中的 @wraps 到底是個(gè)啥東西?
只要你讀得很快
你可能在隨意的 Python 代碼中見過這個(gè) @wraps 的東西,你可能想知道這到底是什么?
函數(shù)有元數(shù)據(jù)
元數(shù)據(jù)指的是函數(shù)本身的數(shù)據(jù)。
def apple():
'''a function that prints apple'''
print('apple')
print(apple.__name__) # apple
print(apple.__doc__) # 打印apple的函數(shù)
例子包括函數(shù)名 .__name__ 或函數(shù) docstring .__doc__
裝飾器如何工作
裝飾器用于改變函數(shù)的行為方式,而無需修改任何源代碼。
def greet(name):
return 'hello ' + name
print(greet('tom')) # hello tom
在這里,我們有一個(gè)普通的 greet 功能
def add_exclamation(func):
def wrapper(name):
return func(name) + '!'
return wrapper
@add_exclamation
def greet(name):
return 'hello ' + name
print(greet('tom')) # hello tom!
我們通過在 greet() 上添加 @add_exclamation 來用 add_exclamation() 來裝飾 greet()。這里,add_exclamation 是裝飾器,greet 是被裝飾的函數(shù)。
請(qǐng)注意,greet() 的行為已經(jīng)改變,而我們根本沒有編輯 greet() 的源代碼。這是裝飾器的功勞。
裝飾語法魔術(shù)
def add_exclamation(func):
def wrapper(name):
return func(name) + '!'
return wrapper
@add_exclamation
def greet(name):
return 'hello ' + name
print(greet('tom')) # hello tom!
這是完全相同的:
def add_exclamation(func):
def wrapper(name):
return func(name) + '!'
return wrapper
def greet(name):
return 'hello ' + name
greet = add_exclamation(greet)
print(greet('tom')) # hello tom!
注意 “greet = add_exclamation(greet) ”一行。
裝飾會(huì)導(dǎo)致元數(shù)據(jù)丟失
# 沒有裝飾
def greet(name):
'''says hello to someone'''
return 'hello ' + name
print(greet.__name__) # greet
print(greet.__doc__) # says hello to someone
在這里,我們可以順利打印 greet() 的元數(shù)據(jù)。
# 加了裝飾
def add_exclamation(func):
def wrapper(name):
return func(name) + '!'
return wrapper
@add_exclamation
def greet(name):
'''says hello to someone'''
return 'hello ' + name
print(greet.__name__) # wrapper
print(greet.__doc__) # None
用 add_exclamation 裝飾 greet 后,請(qǐng)注意元數(shù)據(jù)發(fā)生了變化。__name__ 變成了 “wrapper”,而 __doc__ 變成了 wrapper 的 docstring。
這是因?yàn)楫?dāng)我們裝飾 greet 時(shí),我們實(shí)際上是在做這件事:
greet = add_exclamation(greet)
我們正在將 greet 重新分配給一個(gè)由 add_exclamation 返回的函數(shù)--wrapper。
這就是為什么當(dāng)我們嘗試打印 greet.__name__ 和 greet.__doc__ 時(shí),會(huì)打印 wrapper 的元數(shù)據(jù)的原因。
@wraps 防止元數(shù)據(jù)在裝飾過程中丟失
from functools import wraps
def add_exclamation(func):
@wraps(func)
def wrapper(name):
return func(name) + '!'
return wrapper
@add_exclamation
def greet(name):
'''says hello to someone'''
return 'hello ' + name
print(greet.__name__) # greet
print(greet.__doc__) # says hello to someone
請(qǐng)注意,盡管使用了 ad_exclamation 裝飾,greet 的元數(shù)據(jù)還是回到了正常狀態(tài)。
更具體地說,@wraps(something) 用 something 的元數(shù)據(jù)覆蓋了函數(shù)的元數(shù)據(jù)。這樣,我們原來函數(shù)的元數(shù)據(jù)就不會(huì)丟失了。