关于Python的修饰器(二)

    修饰器的用途在于提供一种统一的对象修改模式,并用于不同的对象个体。比如对于某个数据加、减、乘等运算结果,根据某种规则执行统一打折折扣,不同的运算可以处理为不同的函数或者对象,再用统一的折扣修饰器对其进行修饰,实现打折运算。其实Python语言本身很多都是采用修饰器完成其功能的。比如类的方法除了常规的由对象拥有和调用的方法外,静态方法和类方法是另外两种不同特性的方法,而Python正是通过内建的方法staticmethed()和classmethod()利用静态修饰器@staticmethod和@classmethod作用于用户定义的普通函数实现对静态函数和类函数的定义的。

    当然,个人觉得遗憾的是修饰器的修饰作用只在对象定义时才起作用,而不能在运行过程中起作用,这点在使用时应该注意。

    Python修饰器对对象进行修饰,所以,除了可以将函数作为修饰器外,还可以将类作为修饰器,完成对类的修饰。此时,我们应该首先对修饰器有一个基本认识:

  1. 修饰器本身是用来对被修饰对象进行某种修饰的,可以完成某些修饰动作,应该首先定义,同时,它必须具有接收被修饰对象的参数。在前面的例子中,我们的修饰器都是函数,它接收另一个函数作为被修饰对象;
  2. 由于修饰器要对被修饰对象进行修饰,因此,修饰器必须知道被修饰对象的相关信息,并且具有对其完成修饰操作的相关权限。或者反过来说,被修饰对象必须符合修饰器对被修饰对象制定的相关标准,并提供给修饰器相关权限,才能使修饰器完成被修饰对象的修饰。所以,使用同一修饰器的被修饰对象在这个层面上讲是同一类的对象。例如,如果被修饰对象是函数并在修饰器中被调用,那被修饰函数的调用参数定义必须符合修饰器中的调用方式。

    根据上述要点,针对类修饰器,我们可以知道有以下几点:

  1. 关于组合问题。理论上,对修饰器和被修饰对象的组合可以有以下几种:修饰器是函数,被修饰对象是函数;修饰器是函数,被修饰对象是类;修饰器是类,被修饰对象是类;修饰器是类,被修饰对象是函数;
  2. 对于修饰器是函数,修饰对象是类的情况。要解决的第一个问题是,函数必须定义一个被修饰的类作为参数;第二是要在函数中完成对这个类的某种修饰,需要能够访问类的一些特性。为了解决这两个问题,Python通过在修饰器函数中定义一个内嵌的类,而被修饰对象类在经由修饰器修饰后,实际效果是由这个内嵌的类代替了被修饰的对象类而完成的。比如有如下代码:

       def Dec(cls):

              class innerClass:

                     def inMthd(self);

                            print('我是:',self)

              return innerClass

 

       @class outClass:

              def outMthd(self):

                     print('我是:',self)

 

       c=outClass()

    查看c可以发现,c其实已经被取代为innerClass类,这是由于修饰器的作用为c=Dec(c)产生的,所以c的所有特性、方法都已经与outClass无关,例如只具有inMthd方法而不具有outMthd方法了。不过由于修饰器的一般用法是看做利用修饰器对修饰对象进行修改,在使用中我们从逻辑上一般认为它是修改过后的outClass类对象,因此在程序代码中自然还是会调用outClass的方法,使用outClass的特性,所以为了实现这个目的,我们只有通过将innerClass的方法和特性定义为与outClass要使用的方法和特性相同才能达到目的。这里有点像由innerClass重载覆盖了outClass,但又不是通过继承完成的。

  1. 对于修饰器是类,被修饰对象是类或者函数的情况。此时首先要解决的问题是如何向一个类传递被修饰对象参数。这个问题通过修饰器类的__init__初始化方法解决,即在修饰器类实例化的时候,向其传递被修饰对象(类或者函数)。而如何完成对被修饰对象的修改,以及修改时如果需要向修饰器传递更多的被修饰对象的信息,则利用修饰器类的object.__call__(self,[args...])方法。该方法允许使用圆括号向其实例传递参数,并实现方法的调用。如下所示:

       class myClass:

              def __call__(self,*gargs):

                     print(args,end=' ')

              print()

 

       c=myClass()  #创建myClass实例

       c()  #调用__call__,没有参数

       c('1',[1,2,3])  #调用__call__,两个参数

    有了该函数,就可以让被修饰对象向修饰器传递更多的参数。同时,修饰器对被修饰对象的修改也是在这个方法内实现的。如果被修饰对象是类,则可以在修饰器的__call__定义内嵌的类,完成修改后并将其返回;如果被修饰对象是函数,则可以在其中内嵌函数定义并将其返回。如下所示:

       class cDec:  #被修饰对象为类的修饰器

              def __init__(self,cls):  #cls为被修饰的类参数

                     pass

              def __call__(self):  #利用该方法完成对被修饰类的修改

                     class inClass:  #修改后的类

                            def print():

                                   print('inClass')

                     return inClass  #返回被修改后的类

       @cDec  #定义被修饰的类

       class ouClass:

              def print():

                     print('ouClass')

       c=ouClass()  #生成ouClass实例

       c.print()  #调用c的方法,结果显示,已经被修改为inClass的方法了

 

       class mDec:  #被修饰对象为函数的修饰器

              def __init__(self,func):  #func为被修饰的函数参数

                     pass

              def __call__(self):

                     def inFunc():  #修改后的函数

                            print('inFunc')

                     return inFunc()

       @mDec  #定义被修饰的函数

       def ouFunc():

              print('ouFunc')

       ouFunc()  #运行可以看出,ouFunc已经被inFunc取代

    上面的两个例子中还应该注意的是,当被修饰对象为类时,应该返回类inClass,而不是返回类的实例inClass(),因为使用ouClass类时本身还有一个如c=ouClass()的实例化步骤,这时如果已经是一个实例而不是类的定义是错误的。而当被修饰对象为函数时,应该返回函数的运行inFunc()而不是返回函数定义inFunc,否则运行被替代后的ouFunc()实际仅仅是类mDec的一个内嵌函数定义,是无法运行的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值