从字面上理解装饰,就是对原有的东西进行装饰,这个装饰可以是增加一些额外的功能,也不需要去改变原来的东西.
那么python中,装饰器也差不多就是这个意思,比如一个函数,功能已经实现好了,那么后续如果别人在使用这个函数的时候,觉得满足不了自己的需求,想要增加点东西,修改原来函数的逻辑不现实,工作量大,有没有一种方法,不改变原来函数的功能的基础上,增加一些功能?装饰器就可以实现!
装饰器的应用场景:插入日志、性能测试、事务处理、权限校验等
装饰器实际就是一个函数.一个闭包函数.
满足闭包所包含的特点:
①有内嵌函数
②外部函数返回值为内部函数的引用
③内部函数使用到了外部函数的变量,只是这里的变量是被装饰的原函数而已.
④装饰器函数也就是外部函数的形参是被装饰的原函数,传递进来之后,被内部函数使用
内部函数的作用就是装饰原函数,所以函数体中有原函数的调用,以及新增加的功能.
所以python装饰器的本质就是一个函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外的功能,装饰器的返回值也是一个函数对象(函数的引用)
装饰器有以下几种使用方法:
①直接对函数进行装饰:函数装饰器
ef Decorator(f):
def inner():
print("装饰开始....")
f()
print("装饰结束....")
return inner
@Decorator #fun = Decorator(fun)
def fun():
print("原函数,待装饰...")
fun()
运行结果:
装饰开始.... 原函数,待装饰... 装饰结束....
上面代码执行步骤:
①先执行fun()函数调用,fun的值就是函数引用,去内存中找的时候发现fun函数被装饰了,fun不是普通的fun,被装饰器装饰成了fun = Decorator(fun)
②解析Decorator(fun),由于Decorator也是一个函数引用,那么此时也去调用,传参为fun,传进去之后,返回值为inner函数的引用,此时fun拿到的值是inner,并没有被调用.
③fun = inner,然后fun()解析成inner(),去调用inner函数,而inner是闭包函数,保留着上上一次外部函数调用的环境变量fun,此时才开始真正的调用inner函数,执行打印第一条,第二条f(),其实就是fun(),外部的原函数也就是被装饰的函数,此时被调用,接着执行最后一句打印,程序执行结束.
注意一个事项:
print(fun.__name__)
在代码中加入这条语句的话,打印的结果是inner,不符合我们的预期,我们不想看到内部装饰器中的闭包函数,预期是打印fun,此时可以在inner函数的上方加上一条语句:
from functools import wraps #需要导入这个模块
def Decorator(f):
@wraps(f) #加入这条语句
def inner():
print("装饰开始....")
f()
print("装饰结束....")
return inner
@Decorator #fun = Decorator(fun)
def fun():
print("原函数,待装饰...")
fun()
print(fun.__name__)
运行结果:
装饰开始.... 原函数,待装饰... 装饰结束.... fun #可以看到符合我们的预期
②对类中的方法进行装饰:其实也就是函数装饰器,只是这个函数在类中,被称为方法,对方法进行装饰而已.
def Decorator(f):
def inner(c_instan):
print("装饰开始....")
f(c_instan)
print("装饰结束....")
return inner
class A(object):
@Decorator #fun = Decorator(fun)
def fun(self):
print("类中的方法,待装饰")
a = A()
a.fun()
运行结果:
装饰开始.... 类中的方法,待装饰 装饰结束....
上述代码执行步骤分解:
第一步:创建一个对象a,然后a.fun()开始调用对象方法fun,由于fun被装饰器装饰了,但是fun不是通常的fun,它的值是fun = Decorator(fun)
第二步:会去调用装饰器函数,返回值是inner
第三步:a.fun()就是inner(),这里和函数装饰器不一样的是,有一个隐藏的参数在a.fun(self),所以在inner函数调用的时候,Inner(self),其实是传入了self的参数,self就是a,那么inner()调用的话,传入的参数也就是a,然后,执行函数体中的第一句话,打印,接着执行原函数,也就是被装饰的函数fun(),这里传入的就是fun(a),这个时候会直接去调用对象a的方法fun,调用结束后,然后执行最后一条打印结束.
③类的装饰器:故名思意就是使用类作为装饰器.
我们知道装饰器就是函数,而函数是可以被调用的,即函数名后面跟(),而类也是可以被调用的.那么实际应用的过程中,应该是调用函数fun(),fun=类名(),这里实际是类实例化了一个对象,那么对象后面加上(),正常是不可以的,因为对象一般都不是可调用的,但是类有一个内置属性方法,只要类中定义了这样一个__call__()方法,那么由该类实例化出来的对象就可以直接加上(),解释为去调用__call__()方法.
所以要想使用类作为装饰器,那么前提是类中需要定义__call__()这个方法.
下面先给出cal这个方法的怎么使用的.
假设没有定义__call__()这个方法.
class A(object):
pass
# def __call__(self, *args, **kwargs):
# print("我是对象调用函数")
a = A()
a() #直接用对象名()调用会报错
运行结果:
Traceback (most recent call last): File "C:/Users/10270/Desktop/py_test/test_10_10.py", line 117, ina() TypeError: 'A' object is not callable
报错信息显示:对象不是可调用对象.
将上面的__call__()方法去掉注释,如下;
class A(object):
def __call__(self, *args, **kwargs):
print("我是对象调用函数")
a = A()
a()
运行结果:
我是对象调用函数
可以看到在直接使用对象名加上()调用的时候,没有报错,且自动去执行了对象方法__call__
下面进入正题:
使用类作为装饰器,①那就是在__call__这个方法中对原函数进行装饰.②初始化参数传入的是函数,那么需要在构造函数中传入该参数.
class A(object):
def __init__(self,f):
self.f = f
def __call__(self, *args, **kwargs):
print("装饰开始....")
self.f()
print("装饰结束....")
@A
def fun():
print("我是被装饰的函数")
fun()
运行结果:
装饰开始.... 我是被装饰的函数 装饰结束....
这里,对于装饰器的名字是可以随便取的.
④装饰器链:也就是说不管是方法还是函数进行装饰,不仅可以装饰一次,还可以使用多个装饰器,也就是增加多个功能,装饰器之间是独立的.
def d1(f1):
def inner1():
print("我是二级装饰器,开始装饰了....")
f1()
print("我是二级装饰器,装饰结束了....")
return inner1
def d2(f2):
def inner2():
print("我是一级装饰器,开始装饰了....")
f2()
print("我是一级装饰器,装饰结束了....")
return inner2
@d2 #fun = d2(d1(fun)) 最终fun()——》d2(d1(fun))()
@d1 #fun = d1(fun)
def fun():
print("我是被装饰的函数")
fun()
运行结果:
我是一级装饰器,开始装饰了.... 我是二级装饰器,开始装饰了.... 我是被装饰的函数 我是二级装饰器,装饰结束了.... 我是一级装饰器,装饰结束了....
代码执行的步骤分析其实和上面是一样的,这里就省略了,整个过程其实就是一点一点的剥洋葱,装饰器就相当于洋葱的皮,最里面的是原函数,外面一层一层的都是装饰器.感觉有点像递归函数的感觉.
下面是使用类作为装饰器的装饰器链:
# 类装饰器链
class Decorator2(object):
def __init__(self, f):
self.f = f
def __call__(self, *args, **kwargs):
print("一级装饰器调用开始....")
self.f()
print("一级装饰器调用结束....")
class Decorator(object):
#初始化 构造函数
def __init__(self,f):
self.f = f
def __call__(self, *args, **kwargs):
print("二级调用开始.....")
self.f()
print("二级调用结束.....")
@Decorator2 #fun = Decorator2(Decorator(fun))()
@Decorator #fun = Decorator(fun) Decorator(fun)()
def fun():
print("待装饰的函数")
fun()
运行结果:
一级装饰器调用开始.... 二级调用开始..... 待装饰的函数 二级调用结束..... 一级装饰器调用结束....
最后贴上别人写的相关的文章,写的也很不错:https://www.cnblogs.com/lianyingteng/p/7743876.html



