你不一定全知道的四种Python装饰器实现详解_python 装饰器 再内存中的实现方法(1)

二、函数的函数装饰器

装饰器包含装饰对象和被装饰对象,最简单的装饰器是用装饰器函数装饰被装饰函数,在这种场景下,装饰器为函数装饰器,被装饰对象也是函数。

2.1、概述

函数装饰器就是一个特殊的函数,该函数的参数就是一个函数,在装饰器函数内重新定义一个新的函数,并且在其中执行某些功能前后或中间来使用被装饰的函数,最后返回这个新定义的函数。装饰器也可以称为函数的包装器,实际上就是在被装饰的函数执行前或后增加一些单独的逻辑代码,以使得被装饰函数执行后最终的结果受到装饰函数逻辑的影响以改变或限定被装饰函数的执行结果。

2.2、装饰器定义语法
@decoratorName
def originalFunction(\*args,\*\*kwargvs):
    函数体

2.3、装饰器语法解释
  1. 参数是一个函数对象;
  2. 封闭函数内部存在一个嵌套函数,该嵌套函数内会调用封闭函数参数指定的函数,并添加额外的其他代码(这些代码就是装饰);
  3. 嵌套函数的参数必须包含originalFunction的参数,但不能带被装饰对象originalFunction;
  4. 嵌套函数返回值必须与封闭函数参数指定函数的返回值类似,二者符合鸭子类型要求(关于鸭子类型请参考《https://blog.csdn.net/LaoYuanPython/article/details/91350122 第7.3节 Python特色的面向对象设计:协议、多态及鸭子类型》);
  5. 封闭函数的返回值必须是嵌套函数。
  • 装饰器函数的定义参考如下形式:
def decoratorName(originalFunction,\*args,\*\*kwargvs):
    def closedFunction(\*args,\*\*kwargvs):
        ...  #originalFunction函数执行前的一些装饰代码
        ret = originalFunction(\*args,\*\*kwargvs)
        ... #originalFunction函数执行的一些装饰代码
        return ret
    return closedFunction

其中decoratorName是装饰器函数,originalFunction是被装饰的函数,closedFunction是装饰器函数内的嵌套函数。

  • 装饰器定义的语法本质上等同于如下语句:
    originalFunction = decoratorName(originalFunction)
2.4、多层装饰器的使用

在一个函数外,可以顺序定义多个装饰器,类似如:

@decorator1
@decorator2
@decorator3
def originalFunction(\*args,\*\*kwargvs):
    函数体

这种多个装饰器实际上就是叠加作用,且在上面的装饰器是对其下装饰器的包装,以上定义语句效果等同于如下语句:

originalFunction = decorator3(originalFunction)
originalFunction = decorator2(originalFunction)
originalFunction = decorator1(originalFunction)

也即等价于:

originalFunction = decorator1(decorator2(decorator3(originalFunction)))

三、类的函数装饰器
3.1、定义

函数装饰器除了给函数加装饰器(使用函数名作为装饰器函数的参数)外,还可以给类加函数装饰器,给类加函数装饰器时,将类名作为装饰器函数的参数,并在装饰器函数内定义一个类如wrapClass,该类称为包装类,包装类的构造函数中必须调用被装饰类来定义一个实例变量,装饰器函数将返回包装类如wrapClass。

3.2、类的函数装饰器案例1
def decorateFunction(fun, \*a, \*\*k):
    class wrapClass():
        def \_\_init\_\_(self, \*a, \*\*k):
            self.wrappedClass=fun(\*a, \*\*k)
        def fun1(self,\*a, \*\*k):
            print("准备调用被装饰类的方法fun1")
            self.wrappedClass.fun1(\*a, \*\*k)
            print("调用被装饰类的方法fun1完成")
    return wrapClass

@decorateFunction
class wrappedClass:
    def \_\_init\_\_(self ,\*a, \*\*k):
        print("我是被装饰类的构造方法")
        if a:print("构造方法存在位置参数:",a)
        if k:print("构造方法存在关键字参数:",k)
        print("被装饰类构造方法执行完毕")
    def fun1(self,\*a, \*\*k):
        print("我是被装饰类的fun1方法")
        if a:print("fun1存在位置参数:",a)
        if k:print("fun1存在关键字参数:",k)
        print("被装饰类fun1方法执行完毕")

    def fun2(self,\*a, \*\*k):
        print("我是被装饰类的fun2方法")


针对以上被装饰函数装饰的类wrappedClass,我们执行如下语句:

>>> c1 = wrappedClass('testPara',a=1,b=2)
我是被装饰类的构造方法
构造方法存在位置参数: ('testPara',)
构造方法存在关键字参数: {'a': 1, 'b': 2}
被装饰类构造方法执行完毕
>>> c1.fun1()
准备调用被装饰类的方法fun1
我是被装饰类的fun1方法
被装饰类fun1方法执行完毕
调用被装饰类的方法fun1完成
>>> c1.fun2()
Traceback (most recent call last):
  File "<pyshell#37>", line 1, in <module>
    c1.fun2()
AttributeError: 'wrapClass' object has no attribute 'fun2'
>>> 


可以看到被装饰类的相关方法必须在装饰类中调用才能执行,装饰后的类如果装饰函数定义类时未定义被装饰类的同名函数,在装饰后返回的类对象无法执行被装饰类的相关方法。

3.3、类的函数装饰器案例2

上面的案例1是通过将被装饰类的方法在装饰器函数内部的装饰类中静态重新定义方式来实现对被包装类方法的支持,这种情况可以用于装饰器装饰后的类只需调用指定已知方法,但有时我们的装饰器可能用于装饰多个类,只针对构造方法和特定方法在装饰类中重写会导致被装饰类需要调用的功能不能调用,这时我们需要在装饰器中实现一个通用方法来保障被装饰类装饰后能执行被装饰类的所有方法。这就需要借助setattr进行类实例方法的动态定义。

def decorateFunction(fun, \*a, \*\*k):
    class wrapClass():
        def \_\_init\_\_(self, \*a, \*\*k):
            self.wrappedClass=fun(\*a, \*\*k)
            self.decorate() #针对没有重写定义的方法赋值给wrapClass作为实例变量,本案例中为涉及的为fun2方法
        def fun1(self,\*a, \*\*k):
            print("准备调用被装饰类的方法fun1")
            self.wrappedClass.fun1(\*a, \*\*k)
            print("调用被装饰类的方法fun1完成")
        def decorate(self):#针对没有重写定义的方法赋值给wrapClass作为实例变量
            for m in dir(self.wrappedClass):
                if not m.startswith('\_')and m!='fun1':
                    fn = getattr(self.wrappedClass, m)
                    if callable(fn):
                    	 setattr(self, m,fn)
    return wrapClass


@decorateFunction
class wrappedClass:
    def \_\_init\_\_(self ,\*a, \*\*k):
        print("我是被装饰类的构造方法")
        self.name = a[0]
        if a:print("构造方法存在位置参数:",a)
        if k:print("构造方法存在关键字参数:",k)
        print("被装饰类构造方法执行完毕")
        
    def fun1(self,\*a, \*\*k):
        print("我是被装饰类的fun1方法")
        
        if a:print("fun1存在位置参数:",a)
        if k:print("fun1存在关键字参数:",k)
        print("我的实例名字为:",self.name)
        print("被装饰类fun1方法执行完毕")

    def fun2(self,\*a, \*\*k):
        print("我是被装饰类的fun2方法")
        if a:print("fun2方法存在位置参数:",a)
        if k:print("fun2存在关键字参数:",k)
        print("我的实例名字为:",self.name)

针对以上被装饰函数装饰的类wrappedClass,我们执行如下语句:

>>> c1 = wrappedClass('c1',a=1,b=2)
我是被装饰类的构造方法
构造方法存在位置参数: ('c1',)
构造方法存在关键字参数: {'a': 1, 'b': 2}
被装饰类构造方法执行完毕
>>> c2 = wrappedClass('c2',a=12,b=22)
我是被装饰类的构造方法
构造方法存在位置参数: ('c2',)
构造方法存在关键字参数: {'a': 12, 'b': 22}
被装饰类构造方法执行完毕
>>> c1.fun1()
准备调用被装饰类的方法fun1
我是被装饰类的fun1方法
我的实例名字为: c1
被装饰类fun1方法执行完毕
调用被装饰类的方法fun1完成
>>> c2.fun2()
我是被装饰类的fun2方法
我的实例名字为: c2
>>> c1.fun2()
我是被装饰类的fun2方法
我的实例名字为: c1
>>> 

可以看到,除了在装饰类中重写的fun1方法可以正常执行外,没有重写的方法fun2也可以正常执行。

四、函数的类装饰器

除了用函数作为装饰器装饰函数或者装饰类之外,也可以使用类作为函数的装饰器。将类作为函数的装饰器时,需将要装饰的函数作为装饰器类的实例成员,由于装饰后,调用相关方法时实际上调用的是装饰类的实例对象本身,为了确保类的实例对象可以调用,需要给类增加__call__方法。

案例:
class decorateClass:
    def \_\_init\_\_(self,fun):
        self.fun=fun

    def \_\_call\_\_(self, \*a, \*\*k):
        print("执行被装饰函数")


**一、Python所有方向的学习路线**

Python所有方向的技术点做的整理,形成各个领域的知识点汇总,它的用处就在于,你可以按照下面的知识点去找对应的学习资源,保证自己学得较为全面。

![img](https://img-blog.csdnimg.cn/1d40facda2b84990b8e1743f5487d455.png)  
![img](https://img-blog.csdnimg.cn/0fc11d4a31bd431dbf124f67f1749046.png)

**二、Python必备开发工具**

工具都帮大家整理好了,安装就可直接上手!![img](https://img-blog.csdnimg.cn/ff266f529c6a46c4bc28e5f895dec647.gif#pic_center)

**三、最新Python学习笔记**

当我学到一定基础,有自己的理解能力的时候,会去阅读一些前辈整理的书籍或者手写的笔记资料,这些笔记详细记载了他们对一些技术点的理解,这些理解是比较独到,可以学到不一样的思路。

![img](https://img-blog.csdnimg.cn/6d414e9f494742db8bcc3fa312200539.png)

**四、Python视频合集**

观看全面零基础学习视频,看视频学习是最快捷也是最有效果的方式,跟着视频中老师的思路,从基础到深入,还是很容易入门的。

![img](https://img-blog.csdnimg.cn/a806d9b941c645858c61d161aec43789.png)

**五、实战案例**

纸上得来终觉浅,要学会跟着视频一起敲,要动手实操,才能将自己的所学运用到实际当中去,这时候可以搞点实战案例来学习。![img](https://img-blog.csdnimg.cn/a353983317b14d3c8856824a0d6186c1.png)

**六、面试宝典**

![在这里插入图片描述](https://img-blog.csdnimg.cn/97c454a3e5b4439b8600b50011cc8fe4.png)

![在这里插入图片描述](https://img-blog.csdnimg.cn/111f5462e7df433b981dc2430bb9ad39.png)

###### **简历模板**![在这里插入图片描述](https://img-blog.csdnimg.cn/646863996ac44da8af500c049bb72fbd.png#pic_center)




**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**

**[需要这份系统化学习资料的朋友,可以戳这里无偿获取](https://bbs.csdn.net/topics/618317507)**

**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**
  • 17
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值