装饰器
装饰器的开放封闭原则
1.封闭
①不能修改被装饰对象的源代码
②不能更改被装饰对象的调用方式
2.开放
对扩展开放,在封闭的条件下,添加新功能
装饰器
def fn():
print('原有功能')
#装饰器
def outer(tag):
def inner():
print('原函数前新增功能')
tag()
print('原函数后新增功能')
return inner
fn=outer(fn)
fn()
上面就是简单的装饰器,其实本质上,就是一种闭包的运用,将原fn的函数地址存放在tag中,新fn的函数地址就是outer(fn)的返回值,也就是inner函数的地址,所以在之后运行fn其实是在运行inner函数。
语法糖
def outer1(f):
def inner():
f()
print('新功能1')
return inner
def outer2(f):
def inner():
f()
print('新功能2')
return inner
@outer1 # 等同于 fn=outer(outer2(fn))
@outer2 # 等同于 fn=outer2(fn)
def fn():
print('原功能')
上面的@(外层函数)的方法叫做语法糖,语法糖叠加时运行方式自上而下,加载顺序自下而上。
装饰有参数的函数
上述的代码中,fn()都没有带参数,其实加参数一样,逆推一下,如果fn需要加参数,新的fn地址指向inner的函数地址,而调用方式不能改变,所以inner函数要添加与fn相同的参数,tag是原fn的内存地址,所以自然也要添加。从这里,可以得出结论,inner,fn(新),tag(原fn)三者在调用时得到的参数必须相同(因为封闭开放原则,除了添加功能其他都要相同),同理如果原函数有返回值,那么这三者都必须有返回值。(个人感觉,fn和inner就像是参数的中转站,目的是为了把参数赋给tag(原函数)去运行)所以在第一个代码中需要添加参数就是:
def fn(a,b):
print('原有功能')
def outer(tag):
def inner(a,b):
print('原函数前新增功能')
tag(a,b)
print('原函数后新增功能')
return inner
fn=outer(fn)
fn()
可变长参数的装饰器
其实可变长的参数,是为了解决多个函数需要添加同一个装饰,而函数中的形参多样的(一个或者多个又或者有关键词参数)的问题,这里无非就是运用了之前的打包与解包(打散)的方法。如果把上面代码的形参换成*args和**kwargs(之前的可变长参数中有提及)那么无论原函数是否有参数或者有多少参数都可以打包给arg或者kwargs,然后再解包赋给所带该参数的函数。打个比方,虽然原函数fn(a,b)和新函数fn(a,b)(封闭开放原则)的形参都是a,b,而inner,tag(原fn函数地址)的形参都是*args,**kwargs,tag运行时本质上得到的都是打包传递过来后又解包的a,b所带的参数值。
def fn(a,b):
print('原有功能')
def outer(tag):
def inner(*args,**kwargs):
print('原函数前新增功能')
tag(*args,**kwargs)
print('原函数后新增功能')
return inner
fn=outer(fn)
fn(a,b)
带参装饰器
如果我们在添加装饰器的时候,除了需要函数中原有参数外,还需要一些参数用来作为判断条件或者其他用处,但是无论outer还是inner中的形参都是固定的,不能改变。那么有下列的解决方案
①outer内添加变量。
②再套一层,让outer变成闭包,把外层的形参,作为内层的参数。