2.1.2.3 复制原始函数的文档字符串和其他属性

当修饰器返回一个新的函数来替代原始的函数时,一个不好的结果是原始的函数名、原始的文档字符串和原始参数列表都丢失了。通过设置doc(文档字符串)、modulename(完整的函数),以及annotations(关于参数和返回值的额外信息,在Python中可用)可以部分”移植“这些原始函数的属性到新函数的设定。这可以通过使用functools.update_wrapper来自动完成。

In [ ]:In [17]:

  1. import functools
  2. def better_replacing_decorator_with_args(arg):
  3. print "defining the decorator"
  4. def _decorator(function):
  5. print "doing decoration,", arg
  6. def _wrapper(*args, **kwargs):
  7. print "inside wrapper,", args, kwargs
  8. return function(*args, **kwargs)
  9. return functools.update_wrapper(_wrapper, function)
  10. return _decorator
  11. @better_replacing_decorator_with_args("abc")
  12. def function():
  13. "extensive documentation"
  14. print "inside function"
  15. return 14
  1. defining the decorator
  2. doing decoration, abc

In [18]:

  1. function

Out[18]:

  1. <function __main__.function>

In [19]:

print function.__doc__
extensive documentation

在属性列表中缺少了一个重要的东西:参数列表,这些属性可以复制到替换的函数。参数的默认值可以用defaultskwdefaults属性来修改,但是,不幸的是参数列表本身不能设置为属性。这意味着help(function)将显示无用的参数列表,对于函数用户造成困扰。一种绕过这个问题的有效但丑陋的方法是使用eval来动态创建一个封装器。使用外部的decorator模块可以自动完成这个过程。它提供了对decorator装饰器的支持,给定一个封装器将它转变成保留函数签名的装饰器。

总结一下,装饰器通常应该用functools.update_wrapper或其他方式来复制函数属性。