美文网首页程序员上班这点事儿@IT·互联网
python (类/函数)对一个函数应用多个装饰器 执行过程

python (类/函数)对一个函数应用多个装饰器 执行过程

作者: 小灰灰besty | 来源:发表于2017-05-24 18:46 被阅读0次

    网上很多讲解单个python 装饰器的文章,基础知识我们简单描述。

    首先了解装饰器是用来干什么的,装饰器可以增加函数的功能,可以通过函数实现,可以通过类实现,分别介绍两种的简单例子。

    装饰者模式(Decorator Pattern),是在不必改变原类文件和使用继承的情况下,动态的扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象。

    适用装饰者模式场合:
    1.当我们需要为某个现有的对象,动态的增加一个新的功能或职责时,可以考虑使用装饰模式。
    2.当某个对象的职责经常发生变化或者经常需要动态的增加职责,避免为了适应这样的变化,而增加继承子类扩展的方式,因为这种方式会造成子类膨胀的速度过快,难以控制。

    函数的单个装饰器

    使用内嵌包装函数来确保每次新函数都被调用,内嵌包装函数的形参和返回值与原函数相同,装饰函数返回内嵌包装函数对象。

    def deco(func):
        def _deco():
            print("before myfunc() called.")
            func()
            print("  after myfunc() called.")
             #不需要返回func,实际上应返回原函数的返回值
        return _deco
    
    @deco
    def myfunc():
        print(" myfunc() called.")
        return 'ok'
    
    
    myfunc()
    myfunc()
    

    打印结果为:

    before myfunc() called.
    myfunc() called.
    after myfunc() called.
    before myfunc() called.
    myfunc() called.
    after myfunc() called.

    装饰器带类参数

    装饰器带类参数:

    class locker:
        def __init__(self):
            print("locker.__init__() should be not called.")
    
        @staticmethod
        def acquire():
            print("locker.acquire() called.(这是静态方法)")
    
        @staticmethod
        def release():
            print("  locker.release() called.(不需要对象实例)")
    
    
    def deco(cls):
        '''cls 必须实现acquire和release静态方法'''
    
        def _deco(func):
            def __deco():
                print("before %s called [%s]." % (func.__name__, cls))
                cls.acquire()
                try:
                    return func()
                finally:
                    cls.release()
            return __deco
        return _deco
    
    @deco(locker)
    def myfunc():
        print(" myfunc() called.")
    
    myfunc()
    myfunc()
    

    输出结果为:

    before myfunc called [<class 'main.locker'>].
    locker.acquire() called.(这是静态方法)
    myfunc() called.
    locker.release() called.(不需要对象实例)
    before myfunc called [<class 'main.locker'>].
    locker.acquire() called.(这是静态方法)
    myfunc() called.
    locker.release() called.(不需要对象实例)

    对一个函数应用多个装饰器

    def decorator_b(func):
        print ('Get in decorator_b')
        def inner_b(*args, **kwargs):
            print("b %s called." % func.__name__)
            print ('Get in inner_b')
            return func(*args, **kwargs)
        return inner_b
    
    def decorator_a(func):
        print ('Get in decorator_a')
        def inner_a(*args, **kwargs):
            print("a %s called." % func.__name__)
            print ('Get in inner_a')
            return func(*args, **kwargs)
        return inner_a
    
    
    @decorator_b
    @decorator_a
    def f(x):
        print ('Get in f')
        return x * 2
    
    f(1)
    
    

    输出结果:

    Get in decorator_a
    Get in decorator_b
    b inner_a called.
    Get in inner_b
    a f called.
    Get in inner_a
    Get in f

    运行顺序:
    根据自下而上的原则来判断地话,先执行 decorator_a 再执行 decorator_b , 那么会先输出 Get in decotator_a,Get in decorator_b;

    上述装饰器实际等价于:

    f =decorator_b(decorator_a(f))(1)
    

    decorator_a 函数来说,它返回的是个函数对象 inner_a ,这个函数对象是它内部定义的,所以decorator_b执行的是decorator_b(inner_a),那么就可以理解为何打印出来“ b inner_a called.”;

    在decorator_b中的 return func(*args, **kwargs) 为inner_a(f),所以再执行decorator_a 中的inner_a(f),打印出“a f called.”;

    最后执行f(1)。

    类中对一个函数应用多个装饰器

    mylocker.py

    class mylocker:
        def __init__(self):
            print("mylocker.__init__() called.")
    
        @staticmethod
        def acquire():
            print("mylocker.acquire() called.")
    
        @staticmethod
        def unlock():
            print("  mylocker.unlock() called.")
    
    
    class lockerex(mylocker):
        @staticmethod
        def acquire():
            print("lockerex.acquire() called.")
    
        @staticmethod
        def unlock():
            print("  lockerex.unlock() called.")
    
    
    def lockhelper(cls):
        '''cls 必须实现acquire和release静态方法'''
    
        def _deco(func):
            print("%s %s called." % (func.__name__,cls))
            def __deco(*args, **kwargs):
                print("before %s called." % func.__name__)
                cls.acquire()
                try:
                    return func(*args, **kwargs)
                finally:
                    cls.unlock()
    
            return __deco
    
        return _deco
    
    

    test.py

    from mylocker import *
    
    
    class example:
        @lockhelper(mylocker)
        def myfunc(self):
            print(" myfunc() called.")
    
        @lockhelper(mylocker)
        @lockhelper(lockerex)
        def myfunc2(self, a, b):
            print(" myfunc2() called.")
            return a + b
    
    
    if __name__ == "__main__":
        a = example()
        #实例化过程:
        # 第一步lockhelper(mylocker)(myfunc) 打印 myfunc <class '__main__.lockerex'> called. 返回方法__deco,
        # 第二步lockhelper(lockerex)(myfunc2) 打印 myfunc2 <class '__main__.lockerex'> called. 返回方法__deco,
        # 第三步lockhelper(mylocker)(__deco) __deco <class '__main__.mylocker'> called. 返回方法__deco。
    
        a.myfunc()
        print(a.myfunc())
        print(a.myfunc2(1, 2))
        #过程解析:
        #等价于lockhelper(mylocker)(lockhelper(lockerex)(myfunc2))(1,2)
        #第一步 等价于lockhelper(mylocker)(__deco)(1,2) 因为lockerex(myfunc2)返回的是__deco方法
        #lockhelper(mylocker)(lockhelper(lockerex)(myfunc2))(1,2) 执行后打印 before __deco called.
        #传入类为mylocker,执行cls.acquire() 打印 mylocker.acquire() called.
        #func(*args, **kwargs) 中func为lockhelper(lockerex)(myfunc2)
        #执行lockhelper(lockerex)(myfunc2) 打印 before myfunc2 called.
        #传入类为lockerex,执行cls.acquire() 打印 lockerex.acquire() called.
        #传入func(*args, **kwargs) 中func为myfunc2(1,2)
        #执行myfunc2(1,2) 打印 myfunc2() called. 返回 3
        #返回 3 执行lockerex的 cls.unlock() 打印 lockerex.unlock() called.
        #返回 3 执行mylocker的 cls.unlock() 打印 mylocker.unlock() called.
        
        print(a.myfunc2(3, 4))
    

    相关文章

      网友评论

        本文标题:python (类/函数)对一个函数应用多个装饰器 执行过程

        本文链接:https://www.haomeiwen.com/subject/vbpoxxtx.html