• python之装饰器【转】


    装饰器

    先来看一个例子,一个函数f1接收另一个函数f2作为参数,并将该参数f2返回给f2:

    复制代码
    def deco(func):
        print("before myfunc() called.")
        func()
        print("after myfunc() called.")
        return func
    
    def myfunc():
        print("myfunc() called.")
    
    myfunc = deco(myfunc)
    
    myfunc()
    
    myfunc()
    复制代码

    可以使用装饰器(Decorator)语法糖来简化代码:

    复制代码
    def deco(func):
        print("before myfunc() called.")
        func()
        print("after myfunc() called.")
        return func
    
    @deco
    def myfunc():
        print("myfunc() called.")
    
    myfunc()
    
    myfunc()
    复制代码

    跟上面的代码完全一样,也就是说在定义一个函数(比如foo)的时候,加上@deco,就等同于:foo = deco(foo)

    简单的看上面的代码似乎没什么价值,无非就是在Decorator中接收一个函数作为参数,并返回给作为参数的函数本身。

    但如果我们在Decorator中内嵌定义一个新的函数,并将这个新的函数返回给作为参数的函数,那就不一样了,例如:

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

    在这里,myfunc()函数永久的失去了,后续调用myfunc(),真正调用的其实是在Decorator中返回的函数。

    可见,Decorator实际上就是一个函数,一个用来包装函数的函数,返回一个修改之后的函数对象,将其重新赋值原来的标识符,并永久丧失对原始函数对象的访问。

    Decorator是在函数之上的修饰,这些修饰仅是当声明一个函数或者方法的时候,才会应用的额外调用。


    装饰带参数的函数

    上面的例子Decorator装饰的参数都是无参的,下面来看如何装饰带参数的函数,

    复制代码
    def deco(func):
        def _newfunc(a, b):
            print("before myfunc() called.")
            ret = func(a, b)
            print("after myfunc() called. result: %s" % ret)
            return ret
        return _newfunc
    
    @deco
    def myfunc(a, b):
        print("myfunc(%s,%s) called." % (a, b))
        return a + b
    
    myfunc(1, 2)
    复制代码

    如果被装饰函数的参数个数不确定呢,即可变参数的情况:

    复制代码
    def deco(func):
        def _newfunc(*args, **kwargs):
            print("before %s called." % func.__name__)
            ret = func(*args, **kwargs)
            print("after %s called. result: %s" % (func.__name__, ret))
            return ret
        return _newfunc
    
    
    @deco
    def myfunc(a, b=8, c=1):
        print("myfunc2(%s,%s,%s) called." % (a, b, c))
        return a+b+c
    
    
    myfunc(1)
    myfunc(1, 4)
    myfunc(1, 10, 9)
    复制代码

    只要在装饰器中的内嵌函数使用可变参数列表(*args、**kwargs)即可。


    装饰器自身带参数

    这种情况需要嵌套两层函数:

    复制代码
    def deco(arg):
        def _deco(func):
            def _newfunc():
                print("before %s called [%s]." % (func.__name__, arg))
                func()
                print("after %s called [%s]." % (func.__name__, arg))
            return _newfunc
        return _deco
    
    @deco("mymodule")
    def myfunc():
        print("myfunc() called.")
    
    
    myfunc()
    复制代码

    如上面的例子,执行myfunc 函数实际上是执行:deco("mymodule")(myfunc) ,deco("mymodule") 返回的是_deco函数,再调用该函数_deco(myfunc),最终返回的是_newfunc函数。

    函数也是对象,有__name__属性,如果执行 myfunc.__name__ 语句,会发现返回的是 "_newfunc",如果要保留被装饰函数的原来名字,可以在装饰器中加入一句:@functools.wraps(func)

    复制代码
    def deco(arg):
        def _deco(func):
            @functools.wraps(func)
            def _newfunc():
                print("before %s called [%s]." % (func.__name__, arg))
                func()
                print("after %s called [%s]." % (func.__name__, arg))
            return _newfunc
        return _deco
    复制代码

    如果装饰器的参数是类类型:

    复制代码
    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()
    复制代码

    同时使用多个装饰器

    复制代码
    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):
            def __deco(*args, **kwargs):
                print("before %s called." % func.__name__)
                cls.acquire()
                try:
                    return func(*args, **kwargs)
                finally:
                    cls.unlock()
            return __deco
        return _deco
    
    
    
    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
    
    a = example()
    a.myfunc()
    print(a.myfunc())
    print(a.myfunc2(1, 2))
    print(a.myfunc2(3, 4))
  • 相关阅读:
    WPF 程序 处理未捕获异常,和程序莫名终止说拜拜
    CSS块级元素和行内元素
    Memcache安全配置
    ASP.NET MVC3默认提供了11种ActionResult的实现
    css position: absolute、relative详解
    用Redis实现Session功能
    编写 WPF DataGrid 列模板,实现更好的用户体验
    CSS3去除手机浏览器button点击出现的高亮框
    OpenCV 视频处理框架
    DataGridView绑定数据源
  • 原文地址:https://www.cnblogs.com/chengjian-physique/p/8186445.html
Copyright © 2020-2023  润新知