2.1.2.2 像类和函数一样实现修饰器

修饰器的惟一一个要求是可以用一个参数调用。这意味着修饰器可以像一般函数一样实现,或者像类用call方法实现,或者在理论上,甚至是lambda函数。让我们比较一下函数和类的方法。修饰器表达式(@后面的部分)可以仅仅是一个名字,或者一次调用。仅使用名字的方式很好(输入少,看起来更整洁等),但是,只能在不需要参数来自定义修饰器时使用。作为函数的修饰器可以用于下列两个情况:

In [1]:

  1. def simple_decorator(function):
  2. print "doing decoration"
  3. return function
  4. @simple_decorator
  5. def function():
  6. print "inside function"
  1. doing decoration

In [2]:

  1. function()
  1. inside function

In [6]:

  1. def decorator_with_arguments(arg):
  2. print "defining the decorator"
  3. def _decorator(function):
  4. # in this inner function, arg is available too
  5. print "doing decoration,", arg
  6. return function
  7. return _decorator
  8. @decorator_with_arguments("abc")
  9. def function():
  10. print "inside function"
  1. defining the decorator
  2. doing decoration, abc

上面两个修饰器属于返回原始函数的修饰器。如果他们返回一个新的函数,则需要更多一层的嵌套。在最坏的情况下,三层嵌套的函数。

In [7]:

  1. def replacing_decorator_with_args(arg):
  2. print "defining the decorator"
  3. def _decorator(function):
  4. # in this inner function, arg is available too
  5. print "doing decoration,", arg
  6. def _wrapper(*args, **kwargs):
  7. print "inside wrapper,", args, kwargs
  8. return function(*args, **kwargs)
  9. return _wrapper
  10. return _decorator
  11. @replacing_decorator_with_args("abc")
  12. def function(*args, **kwargs):
  13. print "inside function,", args, kwargs
  14. return 14
  1. defining the decorator
  2. doing decoration, abc

In [8]:

  1. function(11, 12)
  1. inside wrapper, (11, 12) {}
  2. inside function, (11, 12) {}

Out[8]:

  1. 14

定义_wrapper函数来接收所有位置和关键词参数。通常,我们并不知道被修饰的函数可能接收什么参数,因此封装器函数只是向被封装的函数传递所有东西。一个不幸的结果是有误导性的表面函数列表。

与定义为函数的修饰器相比,定义为类的复杂修饰器更加简单。当一个对象创建后,init方法仅允许返回None,已创建的对象类型是不可以修改的。这意味着当一个被作为类创建后,因此使用少参模式没有意义:最终被修饰的对象只会是由构建器调用返回的修饰对象的一个实例,并不是十分有用。因此,只需要探讨在修饰器表达式中带有参数并且修饰器init方法被用于修饰器构建,基于类的修饰器。

In [9]:

  1. class decorator_class(object):
  2. def __init__(self, arg):
  3. # this method is called in the decorator expression
  4. print "in decorator init,", arg
  5. self.arg = arg
  6. def __call__(self, function):
  7. # this method is called to do the job
  8. print "in decorator call,", self.arg
  9. return function

In [10]:

  1. deco_instance = decorator_class('foo')
  1. in decorator init, foo

In [11]:

  1. @deco_instance
  2. def function(*args, **kwargs):
  3. print "in function,", args, kwargs
  1. in decorator call, foo

In [12]:

  1. function()
  1. in function, () {}

与通用规则相比(PEP 8),将修饰器写为类的行为更像是函数,因此,他们的名字通常是以小写字母开头。

在现实中,创建一个新类只有一个返回原始函数的修饰器是没有意义的。人们认为对象可以保留状态,当修饰器返回新的对象时,这个修饰器更加有用。

In [13]:

  1. class replacing_decorator_class(object):
  2. def __init__(self, arg):
  3. # this method is called in the decorator expression
  4. print "in decorator init,", arg
  5. self.arg = arg
  6. def __call__(self, function):
  7. # this method is called to do the job
  8. print "in decorator call,", self.arg
  9. self.function = function
  10. return self._wrapper
  11. def _wrapper(self, *args, **kwargs):
  12. print "in the wrapper,", args, kwargs
  13. return self.function(*args, **kwargs)

In [14]:

  1. deco_instance = replacing_decorator_class('foo')
  1. in decorator init, foo

In [15]:

  1. @deco_instance
  2. def function(*args, **kwargs):
  3. print "in function,", args, kwargs
  1. in decorator call, foo

In [16]:

  1. function(11, 12)
  1. in the wrapper, (11, 12) {}
  2. in function, (11, 12) {}

像这样一个修饰器可以非常漂亮的做任何事,因为它可以修改原始的函数对象和参数,调用或不调用原始函数,向后修改返回值。