py 装饰器.md

  1. functools.wraps定义函数装饰器
  2. 类装饰器
  3. 装饰类
  • 常见装饰器
  • 参考: https://blog.csdn.net/u013205877/article/details/78872278

    一个非常简单的示例

    def funA(fn):
        print('A')
        fn() # 执行传入的fn参数
        return 'fkit'
    '''
    下面装饰效果相当于:funA(funB),
    funB将会替换(装饰)成该语句的返回值;
    由于funA()函数返回fkit,因此funB就是fkit
    '''
    @funA
    def funB():
        print('B')
    print(funB) # fkit

    上面程序使用 @funA 修饰 funB,这意味着程序要完成两步操作:

    • 将 funB 作为 funA() 的参数,也就是上面代码中 @funA 相当于执行 funA(funB)。
    • 将 funB 替换成上一步执行的结果,funA() 执行完成后返回 fkit,因此 funB 就不再是函数,而是被替换成一个字符串。

    被修饰的函数总是被替换成 @ 符号所引用的函数的返回值,因此被修饰的函数会变成什么,完全由于 @ 符号所引用的函数的返回值决定,换句话说,如果 @ 符号所引用的函数的返回值是函数,那么被修饰的函数在替换之后还是函数。

    functools.wraps定义函数装饰器

    https://www.cnblogs.com/fcyworld/p/6239951.html

    装饰器(decorator)是干嘛的?对于受到封装的原函数来说,装饰器能够在那个函数执行前或者执行后分别运行一些代码,使得可以再装饰器里面访问并修改原函数的参数以及返回值,以实现约束定义、调试程序、注册函数等目标。装饰器一般返回一个包装器(wrapper),而functools.wraps就是装饰包装器的装饰器。

    def tracer(func):
        def wrapper(*args, **kwargs):
            result = func(*args, **kwargs)
            print('%s(%r,%r)->%r'%(func.__name__,args,kwargs,result))
            return result
        return wrapper
    
    @tracer
    def fibonacci(n):
        if n in (0,1):
            return n
        return (fibonacci(n-1)+fibonacci(n-2))
    
    
    fibonacci(3)
    print(fibonacci)
    print('help:')
    help(fibonacci)

    输出

    fibonacci((1,),{})->1
    fibonacci((0,),{})->0
    fibonacci((2,),{})->1
    fibonacci((1,),{})->1
    fibonacci((3,),{})->2
    <function tracer.<locals>.wrapper at 0x0000024BD3A04598>
    help:
    Help on function wrapper in module __main__:
    
    wrapper(*args, **kwargs)

    装饰器正常工作,但是函数的名字变成装饰器中的包装器了!!!help内置函数也失效了
    也就是说,原函数的属性失效了
    如果想要保留原函数的属性,就可以用到functools.wraps了

    加上 @functools.wraps(func)

    import functools
    
    def tracer(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            result = func(*args, **kwargs)
            print('%s(%r,%r)->%r'%(func.__name__,args,kwargs,result))
            return result
        return wrapper
    
    @tracer
    def fibonacci(n):
        if n in (0,1):
            return n
        return (fibonacci(n-1)+fibonacci(n-2))
    
    
    fibonacci(3)
    print(fibonacci)
    print('help:')
    help(fibonacci)

    输出

    fibonacci((1,),{})->1
    fibonacci((0,),{})->0
    fibonacci((2,),{})->1
    fibonacci((1,),{})->1
    fibonacci((3,),{})->2
    <function fibonacci at 0x0000026227A54598>
    help:
    Help on function fibonacci in module __main__:
    
    fibonacci(n)

    保留原函数的属性

    类装饰器

    class decorator(object):
        def __init__(self, func):
            self.func = func
    
        def __call__(self, *args, **kwargs):
            print('before............')
            res = self.func(*args, **kwargs)
            print('after............')
            return res
    
    
    @decorator
    def run():
        print('run............')
    
    if __name__ == "__main__":
        run()
    -----------------------------------
    before............
    run............
    after............
    

    装饰类

    参考: https://www.jianshu.com/p/dd983ecc1104

    常见装饰器

    @property

    把一个getter方法变成属性,只需要加上@property就可以了,此时,@property本身又创建了另一个装饰器@birth.setter,负责把一个setter方法变成属性赋值

    还可以定义只读属性,只定义getter方法,不定义setter方法就是一个只读属性 下面birth是可读写属性,而age就是一个只读属性

    class Student(object):
    
        def __init__(self, birth):
            self._birth = birth
    
        @property
        def birth(self):
            return self._birth
    
        @birth.setter
        def birth(self, value):
            self._birth = value
    
        # 使用时 del Student().birth
        @birth.deleter
        def birth(self):
            del self._birth
    
        @property
        def age(self):
            return 2014 - self._birth

    @staticmethod 和 @classmethod

    使用@staticmethod或@classmethod,就可以不需要实例化,直接类名.方法名()来调用

    • @staticmethod不需要表示自身对象的self和自身类的cls参数,就跟使用函数一样。
    • @classmethod也不需要self参数,但第一个参数需要是表示自身类的cls参数。

    如果在@staticmethod中要调用到这个类的一些属性方法,只能直接类名.属性名或类名.方法名。
    而@classmethod因为持有cls参数,可以来调用类的属性,类的方法,实例化对象等,避免硬编码。

    class A(object):  
        bar = 1  
        def foo(self):  
            print 'foo'  
    
        @staticmethod  
        def static_foo():  
            print 'static_foo'  
            print A.bar  
    
        @classmethod  
        def class_foo(cls):  
            print 'class_foo'  
            print cls.bar  
            cls().foo()  
    
    A.static_foo()  
    A.class_foo()  

    仅供参考
    目录