无参装饰器
有参装饰器
一、在学装饰器前首先需要储备的知识点
1、任意长度位置参数*args、任意长度关键字参数**kwargs
def index(x,y):
print(x,y)
def wrapper(*args,**kwargs):
index(*args,**kwargs) #index(x=11,y=22)
wrapper(11,y=22)
2、名称空间与作用域:名称空间的“嵌套”关系是在函数定义阶段确定的
3、函数对象:把函数当成变量来使用
3.1 可以把函数当成参数传入函数
3.2 可以把函数当成返回值返回
4、函数的嵌套定义:
def outter(func):
def wrapper(): #在函数outter内定义函数wrapper
pass
return wrapper
5、闭包函数:
def outter():
x=11
def wrapper(): #闭函数即wrapper为内嵌函数
print(x) #包函数即wrapper对外层作用域的引用
return wrapper
f=outter()
6、传参的两种方式
6.1 通过参数的形式为函数体传值
def wrapper(x): #通过设置形参的方式接收参数的传入
print(1)
print(2)
print(3)
print(x) #需要传入参数x
wrapper(1) #传入实参1,形参x=1
6.2 通过闭包的方式为函数体传值
def outter(x):
# x=1
def wrapper():
print(1)
print(2)
print(3)
print(x)
return wrapper #return outter内的wrapper那个函数额内存地址
f1=outter(1) #wrapper=outter(1)
f1()
二、装饰器
1、什么是装饰器
将装饰器拆开来说,器指的是具有某种功能的工具、装饰指的是为其他事物添加额外的锦上添花的东西进行点缀,使其更加完善。
总得来说,装饰器指定义的一个函数(当然也可以是类和方法),该函数是用来为其他函数添加额外的功能的
2、为什么要用装饰器
因为在写代码的时候需要遵循开发封闭原则:
2.1 开放:指的是对扩展功能的开发的,即软件可以在原有的功能的基础上添加新的功能
2.2 封闭:指的是对修改源代码和调用方式是封闭的,意味着对象一旦设计完成,就可以独立完成其工作,而不要对其进行修改。
三、如何用装饰器
用一个需求的逐步完成来解释装饰器的使用原理
需求:在不改变index函数的源代码和调用方式的前提下为函数增加统计运行时间的功能
原函数:
import time
def index(x,y):
time.sleep(3)
print(f'index {x} {y}')
#index(11,22)
解决方式一
获得函数运行的开始和结束时的时间,两者之差为函数的运行时间,然后把所有的代码缩进到一个函数体内:
import time
def index(x,y):
start=time.time()
time.sleep(2)
print(f'index {x} {y}')
stop=time.time()
print(stop-start)
index(11,22)
是否满足需求:不满足
问题:虽然没有修改原函数的调用方式,但是修改了函数的源代码
解决方式二
在每次调用index函数的时候都加上统计时间的代码
import time
def index(x,y):
time.sleep(2)
print(f'index {x} {y}')
start=time.time()
index(11,22)
stop=time.time()
print(stop-start)
start=time.time()
index(11,22)
stop=time.time()
print(stop-start)
是否满足需求:满足
问题:犯了写代码的致命问题,重复写相同的代码,没有用到函数去解决,代码显得冗长
解决方式三
将函数调用和统计时间得到代码缩进到另一个函数wrapper内
import time
def index(x,y):
time.sleep(2)
print(f'index {x} {y}')
def wrapper():
start=time.time()
index(11,22)
stop=time.time()
print(stop-start)
wrapper() #调用方式由index(111,222)变成了wrapper()
是否满足需求:不满足
问题:解决了方式二的代码冗长的问题,但对原函数的调用方式进行了修改,wrapper函数写死了不能传入参数。
方式三的进一步优化
1.
import time
def index(x,y,z):
time.sleep(2)
print(f'index {x} {y} {z}')
def wrapper(*args,**kwargs):
start=time.time()
index(*args,**kwargs)
stop=time.time()
print(stop-start)
wrapper(11,22,33)
wrapper(44,55,66)
是否满足需求:不满足
问题:跟方式三一样,改变了原函数的调用方式,而且不能传入其他函数进行统计时间
优化:在将index的参数写活了,原来的index不能接收外部传入的参数,现在可以通过wrapper进行传入
2.
import time
def index(x,y,z):
time.sleep(2)
print(f'index {x} {y} {z}')
def home(name):
time.sleep(4)
print(f'this is {name} page')
def outter(func): #func=index的内存地址
def wrapper(*args,**kwargs):
start=time.time()
func(*args,**kwargs)
stop=time.time()
print(stop-start)
return wrapper
index=outter(index) #index=wrapper的内存地址
home=outter(home) #home=wrapper的内存地址
index(1,2,3)
home('jarry')
是否满足需求:满足
优化:在上述的基础上又将装饰对象给写活了,原来只能装饰index现在能装饰任意函数了
3.
import time
def index(x,y,z):
time.sleep(2)
print(f'index {x} {y} {z}')
def home(name):
time.sleep(4)
print(f'this is {name} page')
def outter(func):
def wrapper(*args,**kwargs):
start=time.time()
res=func(*args,**kwargs)
stop=time.time()
print(stop-start)
return res
return wrapper
home=outter(home) #home这个名字实际上是指向wrapper的内存地址
res=home('tom') #res=warpper('tom')
print('返回值-->',res) #返回值跟调用原函数home的返回值一样
是否满足需求:满足
问题:每次对函数进行装饰需要将装饰器函数的调用赋值给原函数一样的函数名
优化:返回值也变得和调用原函数的返回值一样
语法糖
省去了最后那一次优化的赋值操作。
# 装饰器
import time #index=timmer(index)
def timmer(func):
def wrapper(*args,**kwargs):
start=time.time()
res=func(*args,**kwargs)
stop=time.time()
print(stop - start)
return res
return wrapper
def index(x,y,z):
time.sleep(2)
print(f'index {x} {y} {z}')
index(x=1,y=2,z=3)
总结无参装饰器模版
def outter(func):
def wrapper(*args,**kwargs):
# 1、调用原函数
# 2、为其增加新功能
res=func(*args,**kwargs)
return res
return wrapper
@auth
def index():
print('from index')
index()