1.什么是装饰器
装饰器是一个函数,这个函数的主要作用是包装另一个函数或类。
包装的目的是在不改变原函数名的情况下改变被包装对象的行为。
接收一个函数,内部对其包装,然后返回一个新函数,这样子动态的增强函数功能。
通过高阶函数传递函数参数,新函数添加旧函数的需求,然后执行旧函数。
2.什么时候使用装饰器
当你想为某个函数增加额外功能,而又不想在这个函数的源代码进行修改,就可以使用装饰器。
举例
假如现在我们有一个test函数:
def test(n:list):
a = 0
for i in range(n):
a += i
return a
现在我们想知道当n为1000000时,函数的执行时间,可以这样:
import time
def test(n:int):
a = 0
start = time.time()
for i in range(n):
a += i
end = time.time()
print('耗时:', end-start, '秒')
return a
print(test(1000000))
结果为:
假如我们要多个函数都这样做,直接去修改函数就会很麻烦,这时候,我们就可以使用装饰器:
import time
from functools import wraps
def timethis(func):
@wraps(func)
def wrapper(*args, **wargs):
start = time.time()
result = func(*args, **wargs)
end = time.time()
print(func.__name__, '耗时:', end-start, '秒')
return result
return wrapper
@timethis
def test(n:int):
a = 0
for i in range(n):
a += i
return a
print(test(1000000))
结果:
可以看到,我们没有改变函数,只是在它上面加了一个@timethis,就拥有了计算时间的功能。这样,只要在需要计算时间的函数前加上这个装饰器就可以了,而不用依次去修改每个函数。
使用装饰器实现一个单例模式
def singleton(cls):
instance = {}
def wapper(*args, **kwargs):
if cls not in instance:
instance[cls] = cls(*args, **kwargs)
return instance[cls]
return wapper
@singleton
class User:
def __init__(self, name):
self.name = name
a1 = User('Tome')
a2 = User('Zhu')
print(a1 is a2) #True
现在又有新的问题
你写了一个装饰器作用在某个函数上,但是这个函数的重要的元信息比如名字、文档字符串、注解和参数签名都丢失了。
解决方案
任何时候你定义装饰器的时候,都应该使用 functools 库中的 @wraps 装饰器来注解底层包装函数。