一、首先,理解装饰器的初衷:不改变原函数的原则
举个栗子,这里有3个函数,实现各自的逻辑功能
def a():
#此方法需要实现某业务A
print('此处代替实现业务A的逻辑代码')
def b():
#此方法需要实现某业务B
print('此处代替实现业务B的逻辑代码')
def c():
#此方法需要实现某业务C
print('此处代替实现业务C的逻辑代码')
下一步,我们想在这里任意一个函数执行时,执行与业务逻辑不相干的某事,比如打印函数名
粗暴一点,为所有函数添加print(func.__name__)
def a():
#此方法需要实现某业务A
print(a.__name__)
print('此处代替实现业务A的逻辑代码')
def b():
#此方法需要实现某业务B
print(b.__name__)
print('此处代替实现业务B的逻辑代码')
def c():
#此方法需要实现某业务C
print(c.__name__)
print('此处代替实现业务C的逻辑代码')
那么问题来了,即便此处将print(func.__name__)方法封装抽离,一样改变了函数abc本身的逻辑,而且当我们想要使用比较复杂的函数(比如计时器函数、超时器)时,abc的代码就会变得难以维护
尝试使用装饰器来实现
def outter(func):
def inner():
print(func.__name__)
func()
return inner
@outter
def a():
#此方法需要实现某业务A
print('此处代替实现业务A的逻辑代码')
@outter
def b():
#此方法需要实现某业务B
print('此处代替实现业务B的逻辑代码')
@outter
def c():
#此方法需要实现某业务C
print('此处代替实现业务C的逻辑代码')
当我们调用a()时,实际执行的是:outter(a)()
,这里的a则是作为参数传入了outter方法 #这其实就是装饰器的核心:将被装饰函数作为参数传入装饰器函数执行
我们继续看outter(a)()的实际执行步骤,看注释
def outter(func):
def inner():
print(func.__name__)
func()
#函数outter会直接执行的,就只有这一句renturn 而已
return inner
'''
1、
可以看到outter(a)的返回值就是函数inner
所以outter(a)()就是inner()
2、
内函数inner所调用的func就是从外函数outter传进来的函数a
那么inner()所做的,就是先打印a的函数名,然后再执行a()
'''
这样,就实现了本文开头所说的,装饰器的不改变原函数原则,借此实现装饰器逻辑与原函数代码的完全分离
这么做最大的好处就是允许我们在业务逻辑执行过程中,添加与业务无关且复杂的操作
二、了解了装饰器基本使用之后,我们再来看看被装饰函数的参数传递
因为被装饰的函数往往具有不同的参数集,所以装饰器函数在处理被装饰函数的参数时,使用(*args,**kwargs)来代替被装饰函数的形参
def outter(func):
def inner(*arg,**keargs):
print(*arg)
print(**kwargs)
print(func.__name__)
func(*args,**kwargs)
return inner
@outter
def a(name,age):
#此方法需要实现某业务A
print('此处代替实现业务A的逻辑代码')
@outter
def b(hight):
#此方法需要实现某业务B
print('此处代替实现业务B的逻辑代码')
@outter
def c(weight):
#此方法需要实现某业务C
print('此处代替实现业务C的逻辑代码')
if __name__ == "__main__":
a(1,age=2)
b(3)
c(weight=4)
可以看到abc函数的实际调用格式完全不可控,使用*args获取函数的非关键字参数,比如1,3,生成list格式传递;使用**kwargs获取关键字参数,例如age=2,weight=4,生成dict格式传递
三、最后来看装饰器函数的参数传递
比较常见的是超时器设置,常用于设置某一行为的超时限制
举个简单的栗子
import time
def timelimited(timeout):
def ouuter(function):
def inner(*args, **kwargs):
print('这里只是证明timeout参数可以正常传进来',timeout)
function(*args, **kwargs)
#注释A
return inner
return ouuter
@timelimited(2) # 设置运行超时时间2S
def a(secs):
print('这里代替业务逻辑')
if __name__ == "__main__":
fn_1(5)
只要在注释A的位置使用多线程实现计时和关闭的功能,一个计时器就开发完成了(了解完整代码参见下一章python计时器)
这里可以看到只要借助装饰器,即便是超时器这样的复杂函数也可以实现,并且完全实现了不改变原函数的初衷