文章目录
-
- 一 被装饰函数带参数
- 二 被装饰函数有返回值
- 三 装饰器带参数
- 四 多个装饰器
前言
看大佬们写的代码看的我是眼花缭乱,不知所云啊。真是感叹,自己还看都看不懂,人家就能写出来了,人与人之间的差距确实有点大呀,为了能够早些看懂大佬们的代码,继续薅头发吧~
阅读这篇可能需要点装饰器基础知识,可以先参考这篇:python中的装饰器(基础装饰器)
一 被装饰函数带参数
在上篇 python中的装饰器(基础装饰器) 中讲的装饰器和被装饰函数都是没有带任何参数的,在实际编程中往往有很多被装饰函数和装饰器都是带有参数的,所以了解它们的带参数情况是很有必要的。下面先从被装饰函数带参数开始,如下。
下面仍然用函数装饰器实现 :计算函数的运行时间 的功能,如下
import time
from functools import wraps
def timer(func):
@wraps(func)
def decorate():
start_time = time.time()
func()
end_time = time.time()
total = end_time - start_time
print("函数运行时间:", total)
return decorate
@timer
def func():
time.sleep(2)
func()
上面就用timer装饰器实现了计算函数运行时间的功能,此时被装饰函数func是没有参数的。
如果把func函数延迟的时间由传入参数决定,那么func函数就变成了下面这样
def func(num):
time.sleep(num)
此时被装饰函数func 带了一个参数num,那么装饰timer又该如何变动呢?timer的变动如下
import time
from functools import wraps
def timer(func):
@wraps(func)
def decorate(num):
start_time = time.time()
func(num)
end_time = time.time()
total = end_time - start_time
print("函数运行时间:", total)
return decorate
@timer
def func(num):
time.sleep(num)
func(3)
>>>
函数运行时间: 3.0008623600006104
与上面相比,这里的装饰器timer 有了些变化:decorate()函数和func()函数都多了一个形参num, 这就是当被装饰函数带有参数时装饰器的写法,装饰函数和被装饰函数也应该有对应的形参变量。
func() 添加对应的形参变量我能理解,因为它本身就是被装饰函数,但是装饰函数 decorate() 为什么也要增加形参变量呢?当时这里想了很久都没有想明白(好菜),网上搜索也没有讲的这么详细的,最后在某天就想着把它拆开一步步分析,分析过程如下
- 1. @timer 还原就是func=timer(func) ,执行timer的时候,timer首先接受一个func变量,然后执行decorate() 函数,decorate() 函数里面调用func(),也就是被装饰函数本身func(),所以decorate()里面的func也要增加对应的形参变量。
- 2. 当timer装饰完成后,返回decorate,即func=decorate,当执行func(3)的时候带了一个实参3,执行func(3)就相当于执行decorate(3),所以decorate()自然也得有对应的形参变量来接受,并且提供给decorate()里面的func使用。
标准的装饰器timer写法是下面这样的,以应对被装饰函数func不确定的传参
import time
from functools import wraps
def timer(func):
@wraps(func)
def decorate(*args, **kargs):
start_time = time.time()
func(*args, **kargs)
end_time = time.time()
total = end_time - start_time
print("函数运行时间:", total)
return decorate
@timer
def func(num):
time.sleep(num)
func(2)
>>>
函数运行时间: 2.0000545978546143
二 被装饰函数有返回值
当被装饰函数有返回值时,我们可以在装饰函数里面用一个变量接受,如下
import time
from functools import wraps
def timer(func):
@wraps(func)
def decorate(*args, **kargs):
start_time = time.time()
res = func(*args, **kargs)
end_time = time.time()
total = end_time - start_time
print("函数运行时间:", total)
return res
return decorate
@timer
def func(num):
time.sleep(num)
return num
print("func的返回值:", func(2))
>>>
函数运行时间: 2.000554084777832
func的返回值: 2
上面用变量res接受func的返回值,然后return res 返回给外面用。
三 装饰器带参数
对于有参数的装饰器,我们需要用两层闭包来写,如下
import time
from functools import wraps
def my_timer(parm):
def timer(func):
@wraps(func)
def decorate(*args, **kwargs):
if parm == 1:
print("这是func1")
start_time = time.time()
func(*args, **kwargs)
end_time = time.time()
total = end_time - start_time
print("函数运行时间:", total)
elif parm == 2:
print("这是func2")
start_time = time.time()
func(*args, **kwargs)
end_time = time.time()
total = end_time - start_time
print("函数运行时间:", total)
return decorate
return timer
@my_timer(1)
def func1():
time.sleep(1)
@my_timer(2)
def func2():
time.sleep(2)
func1()
func2()
>>>
这是func1
函数运行时间: 1.0001475811004639
这是func2
函数运行时间: 2.0003774166107178
这里的timer比上面的timer多了一层外层函数my_timer,它的作用就是和timer构成一个闭包,然后timer和decorate构成一个闭包。
因为装饰函数decorate引用了两个自由变量:parm和func,而它的外层函数timer只能传一个func,timer自然需要外加一层函数来传parm变量,并且timer的外层函数要与timer构成闭包,以让接受的parm变量成为自由变量,从而提供给装饰函数decorate使用。
以上就是装饰器带参数的写法。
四 多个装饰器
有时候一个装饰器还不能满足需求,要多个装饰器一起才能实现,多个装饰器的写法很简单,难的是理解它的执行顺序,如下
def deco_one(func):
print("这是deco_one")
def deco1(*args, **kwargs):
print("这是deco1的开始")
func(*args, **kwargs)
print("这是deco1的结束")
return deco1
def deco_two(func):
print("这是deco_two")
def deco2(*args, **kwargs):
print("这是deco2的开始")
func(*args, **kwargs)
print("这是deco2的结束")
return deco2
@deco_one
@deco_two
def func():
print("Hello world")
func()
先说下多个装饰器的执行顺序:
- 1.不附加在装饰函数里面的是自底向上执行
- 2.在装饰函数里面的,但在被装饰函数func()之前的是自顶向下执行
- 3.执行被装饰函数func()
- 4.在装饰函数里面的,并且在被装饰函数func()之后的是自底向上执行
所以上面的执行结果是
>>>
这是deco_two
这是deco_one
这是deco1的开始
这是deco2的开始
Hello world
这是deco2的结束
这是deco1的结束
python装饰器带参数的就到此结束了,上篇文章:python中的装饰器(基础装饰器)
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)