python装饰器
前言
提示:在看python 一些包的源码时经常会看到 函数或者是类声明的上一行出现“@” 那么这个的作用是什么呢?:
一、装饰器有什么作用?
- 可以在不修改函数内部代码的情况下,修改函数的调用
- 函数运行的日志输出、计算时间……
- 增加函数或者时类的功能
二、装饰器的使用
1.函数式装饰器
代码如下:
data = [i for i in range(10 ** 7)]
def foo(func):
def getTime():
start = time.time()
func()
end = time.time()
print("运行时间 : ",end - start)
return getTime //返回一个callable
@foo
def main():
a = []
for i in data:
a.append(i * 2)
这段程序可用来计算函数的运行时间
装饰器可以将一个函数传入另一个函数,再通过另一个函数返回一个函数最后我们调用的时候使用的就是返回得到的这个函数。
1. 循环 VS 列表推导式 VS map 效率比较
@foo
def circulation():
a = []
for i in data:
a.append(i * 2)
@foo
def listMethod():
a = [i*2 for i in data]
@foo
def mapMethod():
# 需要转化成list 不然时间为0 ,map得到得到是一个可迭代的对象
a = list(map(lambda x:x * 2,data))
得到的结果 列表推导式 < for 循环 < map。(不过可能在不同的场景下的运行速率会有所不同,之前看到map方法的运行速率是排在中间的)
2. 带有参数的装饰器
在上面的例子中我们不好看出来输出的结果到底对应的是哪个,可以通过装饰器传参的形式实现。
# 装饰器部分的函数
def decoration(method):
def foo(func):
def getTime():
start = time.time()
func()
end = time.time()
print(f"{method}运行时间 : ",end - start)
return getTime
return foo
@decoration("circulation")
def circulation():
a = []
for i in data:
a.append(i * 2)
@decoration("listMethod")
def listMethod():
a = [i*2 for i in data]
@decoration("mapMethod")
def mapMethod():
# 需要转化成list 不然时间为0 ,map得到得到是一个可迭代的对象
a = list(map(lambda x:x * 2,data))
输出的结果:
2.类式装饰器的使用
代码如下(示例):
class Animal():
caration = "动物"
def __init__(self, func) -> None:
func.caration = self.caration
self.c1 = func
//magic方法可以让类也变成callable 即让类可以像函数一样被调用,可以用来修改类内的变量
def __call__(self, *args: Any, **kwds: Any) -> Any:
print("调用")
return self
@Animal
class Dogs():
def worfe():
print("I am a dog")
if __name__ == '__main__':
a = Dogs()
print(a.caration)
print(type(a))
输出的结果为:
可以通过第三个输出,可以推断出经过类装饰器后Dogs类变成了一个Animal类。
下面将增加Animal类中的方法
class Animal():
caration = "动物"
def __init__(self, func) -> None:
func.caration = self.caration
self.worfe = func.worfe
self.c1 = func
print("__call__")
def __call__(self, *args: Any, **kwds: Any) -> Any:
print("调用")
return self
@Animal
class Dogs():
def worfe():
print("I am a dog")
if __name__ == '__main__':
a = Dogs()
a.worfe()
输出结果应该为
__call__
调用
I am a dog
分析
观察输出结果我们知道我们声明得到的a其实就是
Animal
这个类中__call__
函数的返回值。
理解:
a = Dogs() <=> a = Animal(Dogs)()
可以把这段进行替换发现结果是相同的。
总结
装饰器的主要的思想主要就是将一个func或者是class传递到另一个func或者class,并由此修改原函数或者类的一些功能。