Python装饰器

Decorator

Posted by Ysy on September 3, 2018

##前言

装饰器(Decorator)是python的一个重要部分。简单地说:他是利用语法糖,封装函数,以实现函数的功能。他们有助于让我们的代码更简短,也更Pythonic(Python范儿),这篇记录常用功能,以防遗忘

装饰器简单使用(打log)

In [1]:
def logging(func):
    def wrapper():
        print("%s is running" % func.__name__)
        return func()
    return wrapper


@logging
def yancy():
    print("i am yancy")

yancy()

Out[2]:
yancy is running
i am yancy

这个代码里,logging就是一个装饰器函数,他的作用就是在调用方法是打印出方法的名字,使用方法也很简单 直接就在想用使用的方法上加上@logging就可以,这个@就是python的语法糖,装饰器可重复利用性,并增加了程序的可读性。

带参数装饰器(*args, **kwargs)

简单的装饰器可以实现一些简单的功能,但是上面的装饰器有个问题,如果yancy方法有参数,而我们现在装饰器中处理参数, 那就要用到带参数的装饰器了

In [1]:
def logging(level):
    def decorator(func):
        def wrapper(*args, **kwargs):
            print(level)
            return func(*args)
        return wrapper

    return decorator

@logging('yancy')
def foo(name='yancy'):
    print("i am %s" % name)

foo()
Out [2]:
yancy
i am yancy

这里我们通过嵌套一层方法的方式传递参数,传递的参数可以使str、list、dict

类装饰器

装饰器不仅可以是函数,还可以是类,相比函数装饰器,类装饰器具有灵活度大、高内聚、封装性等优点。使用类装饰器主要依靠类的__call__方法,当使用 @ 形式将装饰器附加到函数上时,就会调用此方法。

In [1]:

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

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

@logging
def foo():
    print('yancy')

foo()

Out [2]:
class decorator runing
yancy
class decorator ending

functools.wraps

使用装饰器极大地复用了代码,但是他有一个缺点就是原函数的元信息不见了,比如函数的__name__、参数列表,先看例子:

In [1]:
def logging(func):
    def wrapper():
        print("%s is running" % func.__name__)
        return func()
    return wrapper


@logging
def yancy():
    print("i am yancy")

print(yancy.__name__)

Out [2]:
wrapper

这个问题就大了,用我们老大的一句话就是:怎么解决那,还好我们有functools.wraps

In [1]:
from functools import wraps

def logging(func):
    @wraps(func)
    def wrapper():
        print("%s is running" % func.__name__)
        return func()
    return wrapper


@logging
def yancy():
    print("i am yancy")

print(yancy.__name__)

Out [2]:
yancy

wraps本身也是一个装饰器,它能把原函数的元信息拷贝到装饰器里面的 func 函数中,这使得装饰器里面的 func 函数也有和原函数 foo 一样的元信息了。

装饰器顺序

In [1]:
@a
@b
@c
def f ():
    pass


装饰器的执行顺序很简单的了,就是从上到下