Hello,大家好,我是景天,今天我们来一起聊一聊python中的装饰器。
装饰器 : 主要功能是在不改变原有代码的前提下,为原函数扩展新功能
装饰器(decorator)
定义:装饰器用于拓展原来函数功能的一种语法,返回新函数替换旧函数
优点:在不更改原函数代码的前提下 给函数拓展新的功能
装饰器本质上是一个Python函数(其实就是闭包),它可以让其他函数在不需要做任何代码变动的前提下增加额外功能,
装饰器的返回值也是一个函数对象。装饰器用于有以下场景,比如:插入日志、性能测试、事务处理、缓存、权限校验等场景。
@符号 装饰器的标识符 :
(1) 自动把下面修饰的原函数或者类当成参数传递给装饰器
(2) 把返回的新函数去替换原函数
(1) 装饰器的原型
def kuozhan(_func): def newfunc(): print("吃饭前 ... 饥肠辘辘") _func() print("吃饭后 ... 肚皮撑撑") return newfunc def func(): print("我是屌丝...") func = kuozhan(func) # func = newfunc func() <=> newfunc() func()
定义装饰器,必须是两层函数,内层函数使用外层函数的形参,外层函数将内层函数返回
没调用闭包函数之前,原函数还是原函数
调用之后,原函数成了扩展函数中的一个参数,并且在内层函数中执行。newfunc是个闭包函数,使用了外层函数的形参
(2) @符号的使用
@符号 装饰器的标识符 :
(1) 自动把下面修饰的原函数当成参数传递给装饰器
(2) 把返回的新函数去替换原函数
def kuozhan(_func): def newfunc(): print("厕所前 ... 干净整齐") _func() print("厕所后 ... 臭气熏天") return newfunc @kuozhan def func(): print("我是高富帅...") func()
@扩展函数名 把下面函数当参数传递给装饰器,把下面的代码往上传
(3) 装饰器的嵌套
def kuozhan1(_func): def newfunc(): print("厕所前 ... 人模狗样1") _func() print("厕所后 ... 牛头马面2") return newfunc def kuozhan2(_func): def newfunc(): print("厕所前 ... 面黄肌瘦3") _func() print("厕所后 ... 红光满面4") return newfunc @kuozhan2 @kuozhan1 def func(): print("我是白富美...5") func()
@kuozhan2 先把下面@kuozhan1当做参数传递给kuozhan2
@kuozhan1 把func当做参数传递给kuozhan1,然后再把执行结果传递给装饰器kuozhan2
多个装饰器存在时,代码从下往上执行
#给一个函数添加多个装饰器,等价于kuozhan2(kuozhan1(func))
当一个函数具有两个装饰器时的执行顺序
针对装饰器的外层函数,是从下往上执行
(4) 带有参数的装饰器
原函数和新函数的参数和返回值要保持一一对应
def kuozhan(_func): def newfunc(who,where,eat): print("厕所前 ... 文质彬彬") _func(who,where,eat) print("厕所后 ... 兽性大发") return newfunc @kuozhan def func(who,where,eat): print("{who}在{where}吃{eat}".format(who=who,where=where,eat=eat) ) func("假率先","浴缸","榴莲") # <=> newfunc()
带有参数的装饰器,即是装饰器的内层函数带有参数,并且带有的参数个数要和原函数的参数个数一样,不一样的话报错
参数个数不一致,报错
(5) 带有参数返回值的装饰器
def kuozhan(_func): def newfunc(*args,**kwargs): print("手工耿同学向下面拉屎的同学们致敬 ~") res = _func(*args,**kwargs) print("请使用我的自动便便称重器 ... ") return res #原函数带有返回值,装饰器内层函数也要借助变量将函数执行结果返回 return newfunc @kuozhan def func(*args,**kwargs): dic = {"liuwenbo":"刘文波","zhanglei":"张磊","songjian":"宋健"} lst = [] try: i = 0 for k,v in kwargs.items(): # 键在dic中,再去拼凑字符串 if k in dic: # 人名 + 地点 + 拉的重量 strvar = dic[k] + "在" + args[i] + "拉了" + v #调用处地点少了一个,导致args[2]获取不到值,元祖下标越界,所以需要异常处理,抑制异常 lst.append(strvar) i += 1 except: # print(i) # 2 # print(list(dic.values())) # ['刘文波', '张磊', '宋健'] # print(list(dic.values())[i]) print("{}找不到拉屎的地点而错,请传入他的拉屎地点".format(list(dic.values())[i])) return lst # return ["刘文博在电线杆子下面拉了15吨" , "张磊拉了15斤","宋健拉了15克"] res = func("电线杆子下面","电影院",liuwenbo="15吨",zhanglei="15斤",songjian="15克") #这里地点少了一个 print(res)
(6) 使用类装饰器
class Kuozhan(): def __call__(self,_func): return self.kuozhan2(_func) def kuozhan1(func): def newfunc(): print("厕所前 ... 饥肠辘辘") func() print("厕所后 ... 酒足饭饱") return newfunc def kuozhan2(self,func): def newfunc(): print("厕所前 ... 蓬头垢面") func() print("厕所后 ... 衣衫褴褛") return newfunc
方式一
@Kuozhan.kuozhan1 def func(): print("厕所进行时 .... ") func()
方式二 直接@类名(),通过定义__call__调用来装饰
@Kuozhan() def func(): print("厕所进行时 .... ") func()
过程分析
(7) 带有参数的函数装饰器,用函数将类中的函数装饰一下
def outer(num): def kuozhan(_func): def newfunc1(self): print(self) print("厕所前 ... 老实巴交") _func(self) print("厕所后 ... 浑身哆嗦") def newfunc2(self): print(self) print("厕所前 ... 狂送人头") _func(self) print("厕所后 ... 让二追三") if num == 1: return newfunc1 elif num == 2: return newfunc2 elif num == 3: return "厕所前,洗洗手,厕所后,簌簌口" return kuozhan class MyClass(): @outer(1) # (1)@outer(1) => @kuozhan (2)@kuozhan => func1 = newfunc1 => (3) obj.func1() <=> obj.newfunc1(self) def func1(self): print("向前一小步,文明一大步") @outer(2) def func2(self): print("不冲就打包带走") @outer(3) def func3(self): print("请瞄准后发射,尿到外边,说明你短!") print("<==============>") obj = MyClass() obj.func1() # <=> obj.newfunc1() # print("<==============>") # obj.func2() # print("<==============>") # print(obj.func3)
@outer()来传参,根据不同的参数使用不同的装饰器来对类中的函数来装饰
func3没返回函数,直接返回了属性,直接赋值给变量/打印即可
调用流程
(8) 带有参数的类装饰器,类装饰器修饰类。在flask框架中应用比较多
参数1: 给修饰的类添加成员属性和方法
参数2: 把类中的run方法变成属性
class Kuozhan(): ad = "贵族茅厕,茅厕中的百岁山." def money(self): print("贵族茅厕,包月1100,一小时200元") def __init__(self,num): self.num = num def __call__(self,cls): print(cls) # MyClass if self.num == 1: return self.kuozhan1(cls) elif self.num == 2: return self.kuozhan2(cls) # 参数1的情况 : 添加成员属性和方法 def kuozhan1(self,cls): def newfunc(): # MyClass.ad = "贵族茅厕,茅厕中的百岁山." cls.ad = Kuozhan.ad cls.money = Kuozhan.money return cls() return newfunc # 参数2的情况 : 把方法变成属性; def kuozhan2(self,cls): def newfunc(): if "run" in cls.__dict__: cls.run = cls.run() return cls() return newfunc
方式一
@Kuozhan(1) # => @obj => MyClass = obj(MyClass) class MyClass(): def run(): return "亢龙有悔" obj = MyClass() print(obj.ad) obj.money()
被装饰的类的对象多了方法和属性
返回cls(), 是对象
方式二
@Kuozhan(2) class MyClass(): def run(): return "亢龙有悔" obj = MyClass() print(obj.run)
调用分析,此时,类() 实际上是个函数
MyClass()成了函数,那怎么还可以通过对象调用参数, 那是因为又通过cls() 返回回来了
(9)扩展
虽然MyClass2这个名字替换掉了,但是内存中的该类仍然存在;
class MyClass2(): a = 200 print(id(MyClass2) , "1111111") def func(cls): cls.ad = 90 print(id(cls),"22222") return cls() obj = func(MyClass2) MyClass2 = 100 print(MyClass2) print(obj.a) # 200 print(obj.ad) # 90
类是定义的时候就执行类中的代码
函数是调用的时候才执行函数内部的代码