Python装饰器

1 什么是装饰器

装饰器是可调用的对象,其参数是另一个函数(被装饰的函数)。

装饰器可以让一个Python函数拥有原本没有的功能,我们可以通过装饰器,让

一个平淡无奇的函数变的强大。

2 为什么引入装饰器

引入装饰器会便于开发,便于代码复用。举个例子如下:

def test():
    print('test main process')
    logging.info("func end")

那么假如我还有test1、test2、test3等都需要在函数执行结束后打印

logging.info("func end")

怎么办呢,正常想到的就是,那就都来一句logging,但是这样写代码重复率太高,或者还有一种办法,采用原型模式,但是原型模式的局限性太强。

此时可以考虑另一种写法如下:

def happy_ending(func):
    logging.info("func end")
    func()

def test():
    print('test main process')

happy_ending(test)

那么这么做的坏处是什么?代码可读性变弱了,每次的调用都是end,而不再是真正的执行函数 test

利用函数传参的思想,python用装饰器来解决此类问题

def happy_ending(func):

    def wrapper():
        logging.info("happy ending")
        return func()
    return wrapper

def test():
    print('test main process')

test = happy_ending(test)  # 因为装饰器返回的时函数对象 wrapper,这条语句相当于 test = wrapper
test()                   # 执行test()就相当于执行 wrapper()

Python中提供了语法糖@符号来简化开发,上述版本简化如下:

def happy_ending(func):

    def wrapper():
        logging.info("happy ending")
        return func()
    return wrapper

@happy_ending # 这句话代替了test = happy_ending(test)
def test():
    print('test main process')

test()                   # 执行foo()就相当于执行 wrapper()

3 装饰器适用于解决哪些问题

  • 业务扩展功能的增加
    • 当涉及到函数本身比较复杂,不方便针对函数本身做修改时,利用装饰器模式可以更好的解决这类问题
  • 日志类功能
    • 相对比较通用,节约代码冗余
  • 权限校验
    • 通用的权限校验

4 各种类型的装饰器

4.1 带参数的函数

参数的形式可以是*args、**kwargs这种类型的,也可以指定参数名称,与函数名保持一致即可。

def happy_ending(func):

    def wrapper(a,b):
        logging.info("happy ending")
        return func(a,b)
    return wrapper

@happy_ending # 这句话代替了test = happy_ending(test)
def test(a, b):
    c =  a+b
    print c

test(1,2)                   # 执行foo()就相当于执行 wrapper()

4.2 带参数的装饰器

def happy_ending(level):
    def decorator(func):
        def wrapper(*args, **kwargs):
            if level == "warn":
                print "warn"
            elif level == "info":
                print "info"
            return func(*args)
        return wrapper

    return decorator

@happy_ending(level="warn")
def test(name='hello world'):
    print name

test()

4.3 类装饰器

装饰器不仅可以是函数 也可以是类。

class Happy(object):
    def __init__(self, func):
        self._func = func

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

@Happy
def test():
    print ('test')

test()

4.4 带参数的类装饰器

class Happy(object):
    def __init__(self, status='high'):
        self.status = status

    def __call__(self, func):
        def wrapper(*args, **kwargs):
            print(self.status)
            func(*args, **kwargs)
        return wrapper

@Happy(status='middle')
def test(name='hello'):
    print name

test()

5 装饰器顺序

一个函数可以定义多个装饰器 一个装饰器当然也可以被多个函数定义

装饰器的执行顺序举例如下:

@a
@b
@c
def test() :
	sth

此时的执行顺序是从里到外,等价于:

f = a(b(c(f)))