decorator
是python里面非常有用的语法。理解decorator的核心在于理解它是如何被解
释器调用的。我们这样使用装饰器的时候
@myDecorator
def myFunc(a, b, c):
pass
python将这种语法转换成为 myFunc=myDecorator(myFunc)
, 然后我们可以像平常一样使
用myFunc(a, b, c)
来调用它。从这个转换后的语法我们应该可以看出
基于这些要求,我们可以写一个简单的装饰器
def BlackHoleDecorator(func):
def blackhole(*args, **kwargs):
print "I am a blackhole"
pass
return blackhole
@BlackHoleDecorator
def do_something():
print "In do something"
do_something()
显然BlackHoleDecorator
满足之前所有的要求,因此这段代码可以正常的运行,它的输
出为I am a blackhole
。真正执行的函数是装饰器所定义且返回的blackhole
。
但是正常情况下这应该不是我们所期盼的,decorator
的作用是包装一个函数,除了做其
他的工作之外,应该保留原来函数的功能。因此一般我们看到的装饰器形势应该如下:
def VerboseDecorator(func):
def wrapper(*args, **kwargs):
print "Before {} is called".format(func.__name__)
return_value = func(*args, **kwargs)
print "After {} is called".format(func.__name__)
return return_value
return wrapper
@VerboseDecorator
def do_something():
print "During do_something is called..."
do_something()
print do_something.__name__ # wrapper
在这里没有任何特别的代码,只是简单的将调用参数传递给了func函数,也就是通过
do_something=VerboseDecorator(do_something)
传递的初始函数定义。
但是注意到do_something.__name__
的输出,它不是函数名do_something
,而是
wrapper
。显然通过这种装饰,我们丢失了原来函数的一些属性。比如 docstring,函数
名等等,此时我们需要用到funtools
里面的wraps
函数。
from functools import wraps
def VerboseDecorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
print "Before {} is called".format(func.__name__)
return_value = func(*args, **kwargs)
print "After {} is called".format(func.__name__)
return return_value
return wrapper
@VerboseDecorator
def do_something():
print "During do_something is called..."
do_something()
print do_something.__name__
此时do_something.__name__
输出为正常的函数名。
到这里,你应该掌握了python里面装饰器80%以上的用法。
现在回到myFunc=myDecorator(myFunc)
的用法,这里myDecorator一定需要是一个函数吗?不一定,python里面类的初始化也是使用something(args)
这种写法。因此python里面也可以基于类来定义装饰器。
class VerboseClassDecorator(object):
def __init__(self, func):
self.func = func
def __call__(self, *args, **kwargs):
print "Before {} is called".format(self.func.__name__)
return_value = self.func(*args, **kwargs)
print "After {} is called".format(self.func.__name__)
return return_value
@VerboseClassDecorator
def do_anything():
print "During do_anything is called..."
do_anything()
print do_anything.__name__
我们必须为这个class定义__call__
函数,想象一下如下一个类定义
class Foo():
pass
a = Foo()
a()
这段代码会报错,因为一个类对象是不可调用的,必须添加__call__
方法,通过__call__
,调用初始的函数。
同时do_anything
是一个类对象,使用print do_anything.__name__
在这里会报错。为了像之前一样保留函数的名字,docstring等属性,必须使用functools.update_wrapper
来包装self对象。
import functools
....
def __init__(self, func):
self.func = func
functools.update_wrapper(self, func)
这样,一个基于类的装饰器就写成了。