31-高级特性之装饰器(1)
1. 定义:
Decorator(即装饰器)其实就是闭包实现的一个函数:把{一个待扩展功能的函数Fun}当做参数传入一个外函数Out,在外函数内部定义的内函数In“装饰扩充”Fun,然后返回In。
-
一句话描述:把一个函数A当参数传入另一个函数B,并返回一个函数C
def Out(Func):
def In(args, **kw): #这两个参数其实是给Func()使用的
print("前置修饰操作\n")
Func(args, **kw) #当前置修饰操作一旦完成,就自动执行Func
print("后置修饰操作\n")
return In #把闭包返回出去现在一旦执行Out,就会得到In,再执行In(),就会立刻执行前置修饰操作,接下来得到Func()的执行结果并返回出去
装饰器Out的效果就是:先执行前置操作,再执行Fun(),再执行后置方法
不用语法糖,先手动模拟下装饰器的运行:
def fun_1():
print("I am fun_1\n")
ret = Out(fun_1) #得到In
ret() #执行In使用语法糖
@Out #加装饰器Out
def fun_2():
print("I am fun_2\n\n")
fun_2()其实 @Out 等价于 ret = Out(fun_2), fun_2()等价于 ret()
-
Decorator的效果:
- 遵循“开放封闭原则”:对扩展开放,对已写好的函数封闭
- 一个装饰器写好了,可到处重用
- Decorator的应用:
假设有A函数已经写好了,想为它扩充如下功能:
- 引入日志
- 函数执行时间统计
- 执行函数前预备处理
- 执行函数后清理功能
- 权限校验等场景
- 缓存
实例:
-
实例1:
定义Decorator
def makeBold(func):
def wrapper():
return ''+func()+'' #给文本加粗
return wrapperdef makeItalic(func):
def wrapper():
return ''+func()+'' #把文本设为斜体
return wrapper装饰过程
@makeBold
def test1():
return "I am test1!"@makeItalic
def test2():
return "I am test2!"@makeBold
@makeItalic
def test3():
return "I am test3!"调用函数: 要根据装饰器里面对func()的修饰行为
此处需要接受返回值
r1, r2, r3 = test1(), test2(), test3()
print(r1, '\n', r2, '\n', r3)
- func()即可能是无形参,也可能是带各种参数。为了兼容两种情况,采用
func(*args, **kw)
的方式处理:
- func()即可能是有返回值,也可能是无返回值。为了兼容两种情况,采用
ret=func(), return ret
的方式处理(因为如果函数没有返回值python默认都会return None)
-
为了处理以上“参数和返回值问题”,可编写一个通用装饰器
from time import ctime,sleep
def timefun(func):
def wrapper(args, **kw): #支持可变参数,关键字参数 {实现了对参数的自适应}
print("%s called at %s" % (func.name, ctime()))
ret = func(args, **kw) #若有返回值,接受并待会儿返回;若无,返回None
#...若干操作后
return ret #实现了对返回值的自适应
return wrapper@timefun
def test1(): #无参
print("-----test1----")
test1()
sleep(1)
test1()
print('\n')@timefun
def test2(a,b): #有参数
print("%s %s" % (a,b))
test2(10,20)
sleep(1)
test2(20,30)
print('\n')@timefun
def test3(a,b): #无返回值
print("%s+%s=%s" % (a,b,a+b))
ret = test3(100,200)
sleep(1)
ret = test3(200,300)
print(ret, '\n')@timefun
def test4(a,b): #有返回值
return a -b
ret = test4(1000,2000)
print(ret)
sleep(1)
ret = test4(2000,3000)
print(ret, '\n')