装饰器(decorator)在你希望不修改函数本身的前提下扩展函数的功能时非常有用。decorator就像一个wrapper一样,在函数执行之前或者之后修改该函数的行为,而无需修改函数本身的代码,这也是装饰器名称的来由。

0. Python中@的用法

在介绍装饰器之前,先介绍一下Python中@的用法。 @是一个装饰器,针对函数,起调用传参的作用。@function作为一个装饰器,用来修饰紧跟着的函数(可以是另一个装饰器,也可以是函数定义)。

def funA(desA):
    print("It's funA")

@funA
def funC():
    print("It's funC")

输出结果为:

It's funA

这是因为:@funA 修饰函数定义def funC(),将funC()赋值给funA()的形参。执行的时候由上而下,先定义funA,然后运行funA(funC())。此时desA=funC(),然后funA()输出‘It's funA'

1. 原始状态装饰器

def get_text(name):
   return "lorem ipsum, {0} dolor sit amet".format(name)

def p_decorate(func):
   def func_wrapper(name):
       return "<p>{0}</p>".format(func(name))
   return func_wrapper

# 通过get_text=p_decorate(get_text)的方式覆盖了get_text从而形成了有新功能的同名函数
get_text = p_decorate(get_text)
print get_text("John")

本质上,decorator就是一个返回函数的高阶函数。在上面的示例中,p_decorate是一个函数装饰器,通过get_text=p_decorate(get_text)的方式覆盖了get_text,从而形成了有新功能的同名函数。运行程序会输出:

<p>Outputs lorem ipsum, John dolor sit amet</p>

2. python 修饰符语法糖

def p_decorate(func):
   def func_wrapper(name):
       return "<p>{0}</p>".format(func(name))
   return func_wrapper

@p_decorate
def get_text(name):
   return "lorem ipsum, {0} dolor sit amet".format(name)

print get_text("John")

使用Python的@语法,把decorator置于函数的定义处,相当于执行get_text=p_decorate(get_text)。运行上面的程序,同样会输出:

<p>Outputs lorem ipsum, John dolor sit amet</p>

3. class method装饰器

python中类的方法是一个首参数为self指针的函数。我们可以和普通函数一样去做修饰,但是需要注意的是必须在wrapper函数中考虑self指针参数。

def p_decorate(func):
   def func_wrapper(self):
       return "<p>{0}</p>".format(func(self))
   return func_wrapper

class Person(object):
    def __init__(self):
        self.name = "John"
        self.family = "Doe"

    @p_decorate
    def get_fullname(self):
        return self.name+" "+self.family

my_person = Person()
print my_person.get_fullname()

一个更好的方案是调整代码使得我们的装饰器对于函数或者method同样适用。这可以通过通过将args和*kwargs放到wrapper函数中作为参数来实现,这样可以接受任意个数的参数或者keyword型参数。

def p_decorate(func):
   def func_wrapper(*args, **kwargs):
       return "<p>{0}</p>".format(func(*args, **kwargs))
   return func_wrapper

class Person(object):
    def __init__(self):
        self.name = "John"
        self.family = "Doe"

    @p_decorate
    def get_fullname(self):
        return self.name+" "+self.family

my_person = Person()
print my_person.get_fullname()

4. 向decorator传入参数

from functools import wraps

def tags(tag_name):
    def tags_decorator(func):
        # functools模块包含了wraps函数。wraps也是一个decorator,但是仅仅用于更新wrapping function(func_wrapper)的属性为原始函数的属性(get_text)
        @wraps(func)
        def func_wrapper(name):
            return "<{0}>{1}</{0}>".format(tag_name, func(name))
        return func_wrapper
    return tags_decorator

@tags("p")
def get_text(name):
    return "Hello "+name

print get_text("John")

其中,functools模块的wraps函数也是一个decorator,但是仅仅用于更新wrapping function(func_wrapper)的属性为原始函数的属性(get_text)。

@tags("p") 相当于调用tags(tag_name="p"), 然后返回一个装饰器tags_decorator。然后相当于运行 get_text = tags_decorator(func= get_text)

运行后会输出:

Outputs <p>Hello John</p>