2.1.2.2 像类和函数一样实现修饰器
修饰器的惟一一个要求是可以用一个参数调用。这意味着修饰器可以像一般函数一样实现,或者像类用call方法实现,或者在理论上,甚至是lambda函数。让我们比较一下函数和类的方法。修饰器表达式(@后面的部分)可以仅仅是一个名字,或者一次调用。仅使用名字的方式很好(输入少,看起来更整洁等),但是,只能在不需要参数来自定义修饰器时使用。作为函数的修饰器可以用于下列两个情况:
In [1]:
def simple_decorator(function):
print "doing decoration"
return function
@simple_decorator
def function():
print "inside function"
doing decoration
In [2]:
function()
inside function
In [6]:
def decorator_with_arguments(arg):
print "defining the decorator"
def _decorator(function):
# in this inner function, arg is available too
print "doing decoration,", arg
return function
return _decorator
@decorator_with_arguments("abc")
def function():
print "inside function"
defining the decorator
doing decoration, abc
上面两个修饰器属于返回原始函数的修饰器。如果他们返回一个新的函数,则需要更多一层的嵌套。在最坏的情况下,三层嵌套的函数。
In [7]:
def replacing_decorator_with_args(arg):
print "defining the decorator"
def _decorator(function):
# in this inner function, arg is available too
print "doing decoration,", arg
def _wrapper(*args, **kwargs):
print "inside wrapper,", args, kwargs
return function(*args, **kwargs)
return _wrapper
return _decorator
@replacing_decorator_with_args("abc")
def function(*args, **kwargs):
print "inside function,", args, kwargs
return 14
defining the decorator
doing decoration, abc
In [8]:
function(11, 12)
inside wrapper, (11, 12) {}
inside function, (11, 12) {}
Out[8]:
14
定义_wrapper
函数来接收所有位置和关键词参数。通常,我们并不知道被修饰的函数可能接收什么参数,因此封装器函数只是向被封装的函数传递所有东西。一个不幸的结果是有误导性的表面函数列表。
与定义为函数的修饰器相比,定义为类的复杂修饰器更加简单。当一个对象创建后,init方法仅允许返回None
,已创建的对象类型是不可以修改的。这意味着当一个被作为类创建后,因此使用少参模式没有意义:最终被修饰的对象只会是由构建器调用返回的修饰对象的一个实例,并不是十分有用。因此,只需要探讨在修饰器表达式中带有参数并且修饰器init方法被用于修饰器构建,基于类的修饰器。
In [9]:
class decorator_class(object):
def __init__(self, arg):
# this method is called in the decorator expression
print "in decorator init,", arg
self.arg = arg
def __call__(self, function):
# this method is called to do the job
print "in decorator call,", self.arg
return function
In [10]:
deco_instance = decorator_class('foo')
in decorator init, foo
In [11]:
@deco_instance
def function(*args, **kwargs):
print "in function,", args, kwargs
in decorator call, foo
In [12]:
function()
in function, () {}
与通用规则相比(PEP 8),将修饰器写为类的行为更像是函数,因此,他们的名字通常是以小写字母开头。
在现实中,创建一个新类只有一个返回原始函数的修饰器是没有意义的。人们认为对象可以保留状态,当修饰器返回新的对象时,这个修饰器更加有用。
In [13]:
class replacing_decorator_class(object):
def __init__(self, arg):
# this method is called in the decorator expression
print "in decorator init,", arg
self.arg = arg
def __call__(self, function):
# this method is called to do the job
print "in decorator call,", self.arg
self.function = function
return self._wrapper
def _wrapper(self, *args, **kwargs):
print "in the wrapper,", args, kwargs
return self.function(*args, **kwargs)
In [14]:
deco_instance = replacing_decorator_class('foo')
in decorator init, foo
In [15]:
@deco_instance
def function(*args, **kwargs):
print "in function,", args, kwargs
in decorator call, foo
In [16]:
function(11, 12)
in the wrapper, (11, 12) {}
in function, (11, 12) {}
像这样一个修饰器可以非常漂亮的做任何事,因为它可以修改原始的函数对象和参数,调用或不调用原始函数,向后修改返回值。