装饰器实际上就是为了给某程序增添功能,但该程序已经上线或已经被使用,那么就不能大批量的修改源代码,这样是不科学的也是不现实的,因为就产生了装饰器使得其满足:
  1. 不能修改被装饰的函数的源代码
  2. 不能修改被装饰的函数的调用方式
  3. 满足1、2的情况下给程序增添功能

python装饰器本质上就是一个函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外的功能。

要理解通透Python装饰器,先请大家理解以下4个关键的知识点:

  1. 函数名和变量名是一样的,都是“一个名字对应内存地址中的一些内容”

    - 这里面所说的函数名实际上就是函数的地址,也可以认为是函数的一个标签而已,并不是调用,是个名词。

  2. 对于高阶函数的形式可以有两种:
    1) 把一个函数名当作实参传给另外一个函数(“实参高阶函数”) - 即函数作为实参传入一个函数
    2) 返回值中包含函数名(“返回值高阶函数”) - 即函数作为返回值返回给调用者   

  3. 嵌套函数(闭包函数)指的是在函数内部定义一个函数,而不是调用。

  4. 函数只能调用和它同级别以及上级的变量或函数,也就是说:函数里面的能调用和它缩进一样的和他外部的,而内部的是无法调用的

根据2.1规则,函数名可当做实参把目标函数(即被装饰的函数)传递到另一个函数,然后在另一个函数里面做一些操作之后再执行目标函数

这岂不是满足了装饰器三原则中的第一条:即不修改源代码。那么我们再来看看如何满足装饰器三原则中的第二:即函数调用方式不能改变(函数名字,参数都不可以变更)

我们分析一下知识点1:函数名其实就是一个指针,对应内存地址中的一些内容。

那么我们如果能够改变目标函数(即被装饰的函数)指向的内容,那么函数执行的内容就会改变

结合知识点2.2:函数可以作为返回值返回给调用者。利用这个我们就可以改变函数名指向的内容,即函数名指向了函数调用后的返回值

因此,如果我们在装饰函数里面封装这样的函数:然后在另一个函数里面做一些操作之后再执行目标函数

然后将这个函数返回给调用者,即可达成装饰器三原则中的第三条:满足1、2的情况下给程序增添功能

以下为一些例子辅助理解装饰器:

# -*- coding: utf-8 -*-
def makeBold(func):
    print('----a----')
    def inner():
        print('----1----')
        return '<b>' + func() + '</b>' # func = makeItalic.inner(), test = makeBold.inner()
    
    return inner

print u'***装饰声明***' 
@makeBold # 当解析器执行到@makeBold是认为这是一个装饰器,需要对下面的函数进行装饰,因此将test1函数名作为实参传递给了makeBold,即makeBold(test1)
def test1():
    # test1被修饰时,test1的函数地址指针作为实参传递给makeBold函数
    # makeBold函数执行完成后返回makeBold.inner内联函数地址
    # 此时test1实际指向的地址为makeBold.inner内联函数
    # 即整个装饰过程可简单理解成:test1 = makeBold(test1)
    print('----c----')
    print('----3----')
    return 'hello python decorator'

def makeItalic(func):
    print('----b----')
    def inner():
        print('----2----')
        return '<i>' + func() + '</i>' # func = test()
    return inner
    
@makeBold  # test2 = makeBold(test2`) -> test2 = makeBold.inner(), func = makeItalic.inner()
@makeItalic # test2` = makeItalic(test2)  -> test2` = makeItalic.inner(), func = test2()
def test2():
    print('----c----')
    print('----3----')
    return 'hello python decorator'
#分析:
# 1、执行到@makeBold,将@makeItalic修饰后得到函数地址作为参数传入
# 2、执行到@makeItalic,将函数test2的地址作为参数传入,然后返回被修饰后的函数地址makeItalic.inner
# 3、至此:test2指向的函数为最后被修饰的makeBold.inner, makeItalic.inner则为makeBold的实参,原始test2函数地址则为makeItalic的实参
# 4、因此调用test2()时,首先会执行makeBold.inner()打印:'----1----',然后调用实参func指向的makeItalic.inner函数,返回值为<b>xxx</b>, xxx则为makeItalic.inner函数返回值
# 5、此时makeItalic.inner函数打印:'----2----',然后调用实参func指向的原始test2函数,返回值为<i>xxx</i>, xxx则为test2函数返回值
# 6、此时tes2函数打印:'----c----','----3----',返回值为'hello python decorator'
# 至此调用test2最后得到的返回值为:<b><i>'hello python decorator'</i></b>
# 因为最终的打印结果为:
# ----b----
# ----a----
# ----1----
# ----c----
# ----3----
# <b>hello python decorator</b>


print u'***装饰调用***'    
print test1()
print test2()


print u'***函数装饰类成员函数***'    
# 函数装饰类成员函数
def debug(func):
    def _deco(*args, **kwargs):
        print("[DEBUG]: enter {}()".format(func.__name__))
        self = args[0] # 获取实例对象
        server_url = "http://%s:%s" % (self.host, self.port)
        getattr(self, 'test_func')(args[2])
        func(self, 'test')
    return _deco

class Command(object):
    def __init__(self, host, port=10000):
        self.host = host
        self.port = port

    @debug
    def create(self, data): # create = debug(create) = _deco
        print data

    def test_func(self, data):
        print data
        
cmd = Command("1", 1)
cmd.create("1", "2") # -->_deco(self, '1', '2')

print u'***函数装饰类***'    
# 函数装饰类
def wrapClass(cls): # cls为被修饰的类
    def inner(a):
        print('class name:', cls.__name__)
        return cls(a)
        
    return inner

@wrapClass
class Foo():
    # 被修饰后,Foo的构造函数__init__指针指向了wrapClass.inner函数
    # 所以当实例化Foo对象时(即调用了__init__),执行的函数体为wrapClass.inner
    # 并将__init__的函数地址作为实参传递给了wrapClass函数
    # 即: __init__ = wrapClass(__init___)
    def __init__(self, a):
        self.a = a

    def fun(self):
        print('self.a =', self.a)

# 因__init__ = wrapClass(__init___),此时__init__实际调用了wrapClass.inner,参数为a = 'xiemanR'
# 所以此时执行wrapClass.inner函数体,而wrapClass.inner函数体调用cls(a)
# 其中cls是原始的__init__的函数地址,所以cls(a)执行了类Foo的构造函数
# wrapClass.inner函数返回值为实例Foo的对象,所以 m 则为Foo的实例对象
m = Foo('xiemanR')
m.fun()

print u'***类装饰函数***'    
# 类装饰函数
class ShowFunName():
    def __init__(self, func):
        self._func = func

    def __call__(self, a):
        print('function name:', self._func.__name__)
        return self._func(a)

@ShowFunName # ShowFunName.__init__(Bar)
def Bar(a): # Bar = ShowFunName.__call__(a)
    return a

class Test(object):
    def __init__(self, func):
        print('test init')
        print('func name is %s ' % func.__name__)
        self.__func = func

    def __call__(self, *args, **kwargs):
        print('decorator function')
        self.__func(*args, **kwargs)
        
@Test
def test3(*args, **kwargs):  # test3 = Test.__init__(test3)
    print('this is test func')

test3('test', 'test2', dic1='dic1', dic2='dic2')
print(Bar('xiemanR'))

print u'***类装饰类***'  
# 类装饰类
class ShowClassName(object):
    def __init__(self, cls):
        self._cls = cls

    def __call__(self, a):
        print('class name:', self._cls.__name__)
        return self._cls(a)

@ShowClassName   # ShowClassName.__init__(Foobar.__init__)
class Foobar(object):
    def __init__(self, a): # Foobar.__init__ = ShowClassName.__call__(a)
        self.value = a

    def fun(self):
        print(self.value)

a = Foobar('xiemanR') # ShowClassName.__call__('xiemanR')
a.fun()

def cal_exec_time(func):
    def inner(*args, **kwargs):
        print 'inner:', func.__name__
        func(args[0], args[1:])

    return inner

class DesClass():
    def __init__(self):
        pass

    @cal_exec_time
    def func1(self, arg):
        print 'func1:', arg

    @cal_exec_time
    def func2(self, arg):
        print 'func2:', arg

cls = DesClass()
cls.func1('arg1')
cls.func2('arg2')

 

posted on 2019-10-25 16:14  三井寿  阅读(124)  评论(0编辑  收藏  举报