Python基础——装饰器

什么是python装饰器?

    如果要给一个函数完善功能或向其添加某些功能,我们通常都是直接在函数内部中进行修改、添加代码。如果这个函数是某个项目的核心代码的一部分,从而不允许改变内部的代码,这事应该怎么办呢?这个时候就引入了装饰器这个概念,它可以在不改变原函数的情况下来拓展原函数的功能。装饰器本身是一个函数,而且是一个闭包函数。

一个简单例子


def AAA(fun):
    def BBB():
        print ('---装饰---')
        fun()
        print ('------')
    return BBB

@AAA
def A(): #相当于AAA(A)
    print ("world")
A()
---装饰---
world
------

这里可以看到,装饰函数在使用前需要用@函数名来引出来,装饰函数的参数也是一个函数,由被装饰函数名传入,这样子,即使加入多个装饰器,也不需要重复执行原函数。

原理:首先,开看我们的装饰器函数AAA,该函数接收一个参数fun,其实就是接收一个方法名,AAA内部又定义一个函数BBB,在BBB函数中print后调用传进来的参数fun,同时AAA的返回值为内部函数BBB,其实就是一个闭包函数。然后,再来看一下,在函数A上增加@AAA,那这是什么意思呢?当python解释器执行到这句话的时候,会去调用AAA函数,同时将被装饰的函数名作为参数传入(此时为A),根据闭包一文分析,在执行AAA函数的时候,此时直接把BBB函数返回了,同时把它赋值给A,此时的A已经不是未加装饰时的A了,而是指向了AAA.BBB函数地址。接下来,在调用A的时候,其实调用的是AAA.BBB函数,那么此时就会先执行BBB,然后再调用原来的A,该处的A就是通过装饰传进来的参数A。

带一个参数的函数进行装饰

def AAA(fun):
    def BBB(a):
        print ('---装饰---')
        fun(a)
        print ('------')
    return BBB

@AAA
def A(a): #相当于AAA(A)
    print ("world",a)
A('hello')
---装饰---
world hello
------

带不定长参数的函数进行装饰

def AAA(fun):
    def BBB(*args,**kwargs):
        print ('---装饰---')
        fun(*args,**kwargs)
        print ('------')
    return BBB

@AAA
def A(a,b,c): #相当于AAA(A)
    print ("world",a,b,c)
A('hello','这是原函数','我正在被装饰器装饰')
---装饰---
world hello 这是原函数 我正在被装饰器装饰
------

被多个装饰器装饰

def AAA(fun):
    print ("AAA")
    def BBB(*args,**kwargs):
        print ('---装饰A---')
        fun(*args,**kwargs)
        print ('---A---')
    return BBB

def CCC(fun):
    print ("CCC")
    def DDD(*args,**kwargs):
        print ('---装饰C---')
        fun(*args,**kwargs)
        print ('---C---')
    return DDD

@AAA
@CCC
def A(a,b,c): #相当于 A = AAA(A)
    print ("world",a,b,c)
A('hello','这是原函数','我正在被装饰器装饰')
CCC
AAA
---装饰A---
---装饰C---
world hello 这是原函数 我正在被装饰器装饰
---C---
---A---

当有多个装饰器时,可以发现,先用第二个装饰器(CCC)进行装饰,接着再用第一个装饰器(AAA)进行装饰,而在调用过程中,先执行第一个装饰器(makeBold),接着再执行第二个装饰器(makeItalic)。

多个装饰器的顺序可以参考这篇文章:文章

python内置装饰器

1.@staticmethod

  staticmethod装饰器的功能是去除类的方法默认第一个参数是类的实例,使得该方法成为一个普通的函数。它将类中的方法装饰为静态方法,即类不需要创建实例的情况下,可以通过类名直接引用。到达将函数功能与实例解绑的效果。

class exam(object):
    __name__ = "long"

    @staticmethod
    def say(self):
        print ("hello,world!",self)

exam.say('long')
hello,world! long

可以看到类函数的参数self只是一个普通的参数,并不是类的实例化,如果在调用类函数时不传入一个参数,会报错。

2.@property

  property装饰器可以使类方法变成与类属性一样调用。

class exam(object):
    __name__ = "long"

    @property
    def say(self):
        print ("hello,world!",self.__name__)
a = exam()
a.say
hello,world! long

3.@classmethod

类方法的第一个参数是一个类,是将类本身作为操作的方法。


class exam(object):
    __nlme__ = "long"
    def a(self):
        print ('a函数')

    @classmethod
    def say(cls):
        print ("hello,world!",cls().a())
a = exam()
a.say()
a函数
hello,world! None

cls是类本身,故调用函数a前要先实例化。

将类作为装饰器

   要将类作为装饰器需要在类中定义__call__方法。只有被装饰函数被调用时,才会执行__cal__方法里的内容。


class exam(object):
   def __init__(self,a):
       print("初始化")
       self.a = a

   def __call__(self):
        print ("call方法")
        return self.a()

@exam
def test():
    print ("原函数")
test()
初始化
call方法
原函数

 

posted @ 2018-09-04 00:10  龙~白  阅读(141)  评论(0编辑  收藏  举报