1、装饰器decorator概念
- 在不改变原有函数代码,且保持原函数调用方法的基础上,给原函数增加新的功能(给类增加属性或方法)
- 用一个函数或类去装饰一个旧函数(或类)造出一个新函数(或新类)
- 在原有的函数上加上@符,装饰器会吧线面的函数当作参数传递到装饰器中,@符又称之为语法糖
- 应用场景:引入日志,函数执行时间统计,执行函数前的准备工作,执行函数后的处理工作,权限校验缓存等
2、装饰器的原型
描述器扩展属性,装饰器扩展方法(这句弹幕里看到的)
利用闭包,把函数当作参数传递,并且在函数内去调用传递进来的函数,并返回一个函数
传统用法
#定义一个闭包函数(外函数),有一个f形参,作为要向这个参数传递的参数
def outer(f):
#定义内函数,并在内函数中调用了外函数的参数
def inner():
print('我是外函数中的内函数1')
f()
print('我是外函数中的内函数2')
return inner
#定义一个普通的函数
def old():
print('我是一个普通函数')
old=outer(old)#outer返回了inner函数,赋值给了old
old()#实际调用的是inner函数
结果是:
我是外函数中的内函数1
我是一个普通函数
我是外函数中的内函数2
装饰器用法
#定义一个闭包函数(外函数),有一个f形参,作为要向这个参数传递的参数
def outer(f):
#定义内函数,并在内函数中调用了外函数的参数
def inner():
print('我是外函数中的内函数1')
f()
print('我是外函数中的内函数2')
return inner
#定义一个普通的函数
#增加装饰符
@outer#实际就是把old装进了outer,old函数作为outer函数的实参传给了outer
def old():
print('我是一个普通函数')
old()#实际调用的是inner函数
结果是:
我是外函数中的内函数1
我是一个普通函数
我是外函数中的内函数2
3、案例-统计函数执行时间
#装饰器应用场景-统计函数执行时间
import time
#定义一个统计函数执行时间的装饰器
def runtime(func):
def inner():
start=time.perf_counter()
func()
end=time.perf_counter()-start
print(f'函数的调用执行时间为:{end}')
return inner
@runtime
def func():
for item in range(1,10):
print(item,end=' ')
time.sleep(0.5)
func()#1 2 3 4 5 6 7 8 9 函数的调用执行时间为:4.504389300000001
4、装饰器的嵌套
1、先使用离得近的outer装饰器,装饰love函数,返回了一个inner函数
2、再使用上面的扩展装饰器,装饰上一次返回的inner函数 ,有返回了kzinner函数
#外函数
def outer(func):
def inner():
print('find somebody to talk')
func()
print('talk with him at a place')
return inner
#再定义一个装饰器
def kuozhan(func):
def kzinner():
print('扩展1')
func()
print('扩展2')
return kzinner
#扩展的嵌套,这里装饰符的先后,决定了嵌套的顺序,进而决定了执行语句的顺序
@kuozhan
@outer
def love():
print('falling love with somebody')
love()
结果为:
find somebody to talk
扩展1
falling love with somebody
扩展2
talk with him at a plac
5、装饰器带有参数的函数
如果装饰带有参数的函数,需要在内函数中定义形参,并传递给调用的函数,因为调用原函数等于调用内函数
#定义一个装饰器
def outer(func):
#如果装饰带有参数的函数,需要在内函数中定义形参,并传递给调用的函数,因为调用原函数等于调用内函数
def inner(person):
print('找到一个人')
func(person)
print('找一个地方')
return inner
#使用装饰器
@outer
def love(name):
print(f'和{name}一起畅谈人生')
love('思思')
6、装饰带有多参数的的函数
# 定义一个装饰器
def outer(func):
# 如果装饰带有参数的函数,需要在内函数中定义形参,并传递给调用的函数,因为调用原函数等于调用内函数
def inner(who, name, *args, **kwargs):
print('找到一个人')
func(who, name, *args, **kwargs)
print('找一个地方')
return inner
# 使用装饰器
@outer
def love(who, name, *args, **kwargs):
print(f'{who}和{name}一起畅谈人生')
print('然后去吃了好多美食', args)
print('看了一场电影', kwargs)
love('思思','三多','火锅','辣条','麻辣烫',moive='速度与激情')
结果
找到一个人
思思和三多一起畅谈人生
然后去吃了好多美食 ('火锅', '辣条', '麻辣烫')
看了一场电影 {'moive': '速度与激情'}
找一个地方
7、带有参数的装饰器
如果装饰器需要有参数,那么应该给当前装饰器套个壳,用于接收装饰器的参数
# 定义一个装饰器
# 如果装饰器需要有参数,那么应该给当前装饰器套个壳,用于接收装饰器的参数
def kuozhan(person):
def outer(func):
def inner1():
print('找到一个人')
func()
print('找一个地方')
def inner2():
print('找到另外的一个人')
func()
print('找一个也许还蛮有意思的地方')
#装饰器壳的参数,用于在函数内去做流程控制
if person == '高富帅':
return inner1
else:
return inner2
return outer
# 使用装饰器,装饰器带参
@kuozhan('高富帅') # 使用套壳的装饰器名称
def love():
print('一起畅谈人生')
love()
print('****')
@kuozhan('矮穷矬') # 使用套壳的装饰器名称
def love():
print('一起畅谈人生')
love()
结果:
找到一个人
一起畅谈人生
找一个地方
****
找到另外的一个人
一起畅谈人生
找一个也许还蛮有意思的地方
8、类装饰器装饰函数
Outer()得到一个obj @obj得到obj(love)然后再执行call(love)最后调用inner()
# 定义一个装饰器
class Outer():
#当把该类的对象当作函数调用时,自动触发obj()
def __call__(self, func):
self.func=func#把传进来的函数当作对象的成员方法
return self.inner#返回一个闭包(函数)
#在定义的需要返回的新方法中,去进行装饰和处理
def inner(self,person):
print('找一个人')
self.func(person)
print('找一个地方')
# 使用装饰器
@Outer() #Outer()得到一个obj @obj得到obj(love)然后再执行call(love)最后调用inner()
def love(person):
print(f'和{person}一起畅谈人生')
love('张三丰')#实际调用的事装饰器里的inner函数
print(love)#此时love就是Outer类中的inner方法。<bound method Outer.inner of <__main__.Outer object at 0x000001F4D7C4C460>>
9、用类方法装饰函数
到目前为止,所有的形式的装饰器,包括函数装饰器,类装饰器,类方法装饰器,都有一个共同特点:都是在给函数进行装饰,增加功能
还有一种装饰器,是专门装饰类的,也就是在类的定义的前面是用@装饰器这种语法
# 定义一个装饰器
class Outer():
def newInner(func):
Outer.func = func
return Outer.inner
def inner():
print('找到一个人')
Outer.func()
print('约到一个地方')
# 使用装饰器
@Outer.newInner # 使用Outer.newInner(love)==>Outer.inner
def love():
print('和志同道合者一起畅谈人生')
love()#love()=>Outer.inner()
10、用函数装饰器装饰类
还有一种装饰器,是专门装饰类的,也就是在类的定义的前面是用@装饰器这种语法
装饰器给类装饰,目的是为了不改变类的定义和调用的情况下给类增加新的成员(属性或方法)
# 定义一个函数装饰器,给类增加新的属性和方法
def kuozhan(cls):
def func2():
print('我是在装饰器中追加扩展的新方法,func2')
#把刚才定义的类赋值给类
cls.func2=func2
#再添加一个新属性
cls.name='我是装装饰器添加的新属性name'
#返回时,吧追加类新成员的类返回去
return cls
#使用装饰器
@kuozhan
class Demo():
def func():
print(' 我是demo类中定义的func方法')
Demo.func()#此时调用的Demo类是通过装饰器修改过的类
Demo.func2()#装饰器新添加的方法
print(Demo.name)#装饰器新添加的属性
结果
我是demo类中定义的func方法
我是在装饰器中追加扩展的新方法,func2
我是装装饰器添加的新属性name
11、用类装饰器装饰类
# 定义一个类装饰器,给类增加新的属性和方法
class kuozhan():
def __call__(self, cls):
#把接受的类,赋值给当前对象,作为一个属性
self.cls=cls
#返回一个函数
return self.newFunc
def newFunc(self):
self.cls.name='我是在类装饰器中添加的属性name'
self.cls.func2=self.func2
#返回传递进来的实例化结果,obj,返回的是一个对象,self.cls(),因为self.cls是传进来的类
return self.cls()
def func2(self):
print('我是在类装饰其中追加的方法func2')
#使用类装饰器
@kuozhan()#kuozhan()=》obj==》@obj(Demo)==》__call__(Demo)==>newfunc
class Demo():
def func(self):
print(' 我是demo类中定义的func方法')
obj=Demo()#Demo()=newFunc()==>obj
obj.func()
obj.func2()
print(obj.name)
print(obj)#此时的obj依然是Demo类的实例化对象,只不过经过装饰后,增加了新的属性和方法
结果
我是demo类中定义的func方法
我是在类装饰其中追加的方法func2
我是在类装饰器中添加的属性name
<__main__.Demo object at 0x000001AFB370B400>