• 装饰器


    装饰器(decorator)用于为函数方法或类动态增加功能,在不改动原对象的基础上额外增加一些功能。

    说起装饰器,很多文章都会说到闭包和函数作用域,这里就不绕了,只说一下装饰器的使用。

    大多数装饰器会在内部定义一个函数,然后将其返回。装饰器本质上是一个高阶函数,调用原函数作为参数,返回一个函数对象。

    用法如下,比如定义一个求和函数,额外增加一个判断参数类型的功能,这里提供两种写法,下面一种使用了@符号,@符号是装饰器的语法糖,这只是一种语法上的优化,实际执行过程一样。

    def add(a,b):
        return a+b
    
    def checkParams(fn):
        def wrapper(a,b):
            if isinstance(a, (int,float)) and isinstance(b, (int,float)):
                return fn(a,b)
            else:
                print("unsupported variables")
        return wrapper
    
    add = checkParams(add)
    print(add(3,5))
    
    def checkParams(fn):
        def wrapper(a,b):
            if isinstance(a, (int,float)) and isinstance(b, (int,float)):
                return fn(a,b)
            else:
                print("unsupported variables")
        return wrapper
    
    @checkParams
    def add(a,b):
        return a+b
    
    print(add(3,5))
    

    带参数的装饰器

    在上面的装饰器中,装饰器唯一的参数就是执行业务的函数。装饰器的语法允许在调用时,提供其它参数。

    import logging
    
    def use_logging(level):
        def decorator(func):
            def wrapper(*args,**kwargs):
                if level == "warn":
                    logging.warn("%s is running" % func.__name__)
                return func(*args,**kwargs)
            return wrapper
        return decorator
    
    @use_logging(level="warn")
    def foo(name="foo"):
        print("I am %s" % name)
    
    foo()  
    
    # python3 deco.py 
    WARNING:root:foo is running
    I am foo
    

    带参数的装饰器实际上是对原装饰器的一个函数封装,并返回一个装饰器。


    functools.wraps

    使用装饰器极大地复用了代码,但是它有一个问题,就是原函数的元信息不见了。

    拿最上面的求和函数来说,原函数的__name__应该是add,加上装饰器之后,__name__就变成了wrapper。不难发现,原函数已经被装饰器内部的函数替代。

    functools.wraps 就是用来解决这个问题的,wraps本身也是一个装饰器。它能把原函数的元信息拷贝到装饰器函数中,这使得装饰器函数也有和原函数一样的元信息了。

    一个完整的装饰器写法如下:

    import functools
    
    def checkParams(fn):
        @functools.wraps(fn)
        def wrapper(a,b):
            if isinstance(a, (int,float)) and isinstance(b, (int,float)):
                return fn(a,b)
        return wrapper
    
    @checkParams
    def add(a,b):
        return a+b
    
    print(add(3,5))
    
    print(add.__name__)
    
    # python3 deco.py 
    8
    add
    

    装饰器的调用顺序

    装饰器是可以叠加使用的,这就需要弄明白调用顺序了。

    @a
    @b
    @c
    def f():
        pass
    

    等价于:

    def f():
        pass
    
    f = a(b(c(f)))
    

    最后,提一下内置装饰器,除了上面用的@functools.wraps,还有@property,@classmethod,@staticmethod以及@functools.lru_cache,@functools.singledispatch

    参考:
    https://docs.python.org/3/library/functools.html#functools.wraps
    https://docs.python.org/3/library/functions.html#property

  • 相关阅读:
    推荐下自己的开源框架:DataMapFramework
    真的能无师自通吗?JAVA学习指导系列
    再回首,工作的第一个十年
    2个DataSet中的数据传递问题,请高手们多多指教。
    数据结构小结
    CDQZ_Training 2012524 词编码
    PowerDesigner显示Comment注释
    DDD基本元素
    使用FluorineFx.NET更新FMS中的SharedObject
    如何取消页面缓存
  • 原文地址:https://www.cnblogs.com/keithtt/p/7788952.html
Copyright © 2020-2023  润新知