只要你读得很快
你可能在随意的 Python 代码中见过这个 @wraps
的东西,你可能想知道这到底是什么?
函数有元数据
元数据指的是函数本身的数据。
def apple():
'''a function that prints apple'''
print('apple')
print(apple.__name__) # apple
print(apple.__doc__) # 打印apple的函数
例子包括函数名 .__name__
或函数 docstring .__doc__
装饰器如何工作
装饰器用于改变函数的行为方式,而无需修改任何源代码。
def greet(name):
return 'hello ' + name
print(greet('tom')) # hello tom
在这里,我们有一个普通的 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
是被装饰的函数。
请注意,greet()
的行为已经改变,而我们根本没有编辑 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!
这是完全相同的:
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)
”一行。
装饰会导致元数据丢失
# 没有装饰
def greet(name):
'''says hello to someone'''
return 'hello ' + name
print(greet.__name__) # greet
print(greet.__doc__) # says hello to someone
在这里,我们可以顺利打印 greet()
的元数据
# 加了装饰
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
后,请注意元数据发生了变化。__name__
变成了 “wrapper”,而 __doc__
变成了 wrapper 的 docstring。
这是因为当我们装饰 greet
时,我们实际上是在做这件事:
greet = add_exclamation(greet)
我们正在将 greet
重新分配给一个由 add_exclamation
返回的函数--wrapper
。
这就是为什么当我们尝试打印 greet.__name__
和 greet.__doc__
时,会打印 wrapper
的元数据的原因。
@wraps 防止元数据在装饰过程中丢失
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
请注意,尽管使用了 ad_exclamation
装饰,greet
的元数据还是回到了正常状态。
更具体地说,@wraps(something)
用 something
的元数据覆盖了函数的元数据。这样,我们原来函数的元数据就不会丢失了。