Python函数装饰器(decorator)
装饰器(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>