[Python]装饰器总结

目录

闭包

装饰器

  1.  简单装饰器
  2. 修饰带参函数的装饰器
  3. 本身带参数的装饰器
  4. 类装饰器
  5. 装饰器缺点
  6. 装饰器用途

闭包

说到装饰器就不能忽略闭包,下面先介绍一下闭包的概念:

在一些语言中,在函数中可以(嵌套)定义另一个函数时,如果内部的函数引用了外部的函数的变量,则可能产生闭包。闭包可以用来在一个函数与一组“私有”变量之间创建关联关系。在给定函数被多次调用的过程中,这些私有变量能够保持其持久性。—— 维基百科 

简单来说,就是函数内部套函数,里面的函数用到了外面函数的变量,然后把里面的函数当作对象返回,比如下面的例子:

def Outer(msg):
    def Iner():
        print msg  # msg是外部函数的变量(外部变量)
    return printer  # 返回的是函数,同时延长了外部变量的生命周期
printer = Outer('test')
printer()

上面这个例子展示了基本的闭包,同时闭包也可以下面这样用:

def adder(x):
    def wrapper(y):
        return x + y
    return wrapper

adder5 = adder(5)
# 输出 15
adder5(10)
# 输出 11
adder5(6)

这时我们避免了使用全局变量,使得外部函数的参数x的生命周期得到延长。

装饰器

装饰器其实就是由闭包实现的,下面给出几种不同的装饰器:

  • 最简单的装饰器: 
def Outer(func):
    def wrapper():
        logging.warn("%s is running" % func.__name__)
        return func()
    return wrapper

@Outer
def foo():
    print("hello world")

foo()
  • 修饰带参数函数的装饰器:
def Outer(func):
    def wrapper(*args, **kwargs):
        if level == "warn":
           logging.warn("%s is running" % func.__name__)
        elif level == "info":
           logging.info("%s is running" % func.__name__)
           return func(*args)
        return wrapper

    return decorator

@Outer
def foo(name):
    print("hello world %s" % name)

foo('silly b')
  • 装饰器本身带参数:
def Outer(level):
    def decorator(func):
        def wrapper(*args, **kwargs):
            if level == "warn":
                logging.warn("%s is running" % func.__name__)
            elif level == "info":
                logging.info("%s is running" % func.__name__)
            return func(*args)
        return wrapper

    return decorator

@Outer(level="warn")
def foo(name):
    print("hello world %s" % name)

foo(‘silly b’)
  • 类装饰器:
class Foo(object):
    def __init__(self, func):
        self._func = func

    def __call__(self):
        print ('class decorator runing')
        self._func()
        print ('class decorator ending')

@Foo
def bar():
    print ('bar')

bar()

使用类装饰器主要依靠类的__call__方法,当使用 @ 形式将装饰器附加到函数上时,就会调用此方法。

  • 类装饰器顺序:
@a
@b
@c
def f ():
    pass


# 调用顺序从下到上,c——b——a:
f = a(b(c(f)))
  • 装饰器缺点:

会丢失原函数信息,__docstring__,__name__等都变成wrapper函数的信息了,这时我们需要使用functools.wraps,wraps本身也是一个装饰器,它能把原函数的元信息拷贝到装饰器里面的 func 函数中,这使得装饰器里面的 func 函数也有和原函数有一样的元信息了。

from functools import wraps
def logged(func):
    @wraps(func)
    def with_logging(*args, **kwargs):
        print func.__name__      # 输出 'f'
        print func.__doc__       # 输出 'does some math'
        return func(*args, **kwargs)
    return with_logging

@logged
def f(x):
   """does some math"""
   return x + x * x
  • 装饰器一般用途如下:
  1. 注入参数(提供默认参数,生成参数)
  2. 记录函数行为(日志、缓存、计时等功能)
  3. 预处理/后处理(配置上下文)
  4. 修改调用时的上下文(线程异步或者并行,类方法)

参考文章:

原文地址:https://www.cnblogs.com/lizhenghao126/p/11053551.html