Python decorator handling docstrings
我在装饰师使用docstring时遇到问题。举个例子:
1 2 3 4 5 6 7 8 9 10 11 12
| def decorator(f):
def _decorator():
print 'decorator active'
f()
return _decorator
@decorator
def foo():
'''the magic foo function'''
print 'this is function foo'
help(foo) |
现在,帮助没有按预期显示foo的docstring,它显示:
1 2 3
| Help on function _decorator in module __main__:
_decorator() |
号
没有装饰器,帮助是正确的:
1 2 3 4
| Help on function foo in module __main__:
foo()
the magic foo function |
我知道,函数foo是由修饰器包装的,因此函数对象不再是函数foo。但是,怎样才能像预期的那样获得docstring(和帮助)?
使用functools.wraps()更新装饰器的属性:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| from functools import wraps
def decorator(f):
@wraps(f)
def _decorator():
print 'decorator active'
f()
return _decorator
@decorator
def foo():
'''the magic foo function'''
print 'this is function foo'
help(foo) |
号
另请参见functools的标准库文档。
- 如果foo采用任何论据——它们被_decorator所使用的任何论据所取代,这就不起作用。这是一个问题,尤其是当你想让你的装饰师拿走*args, **kwds的时候。我从来没有找到一种方法来使用functools.wraps来纠正docstring。
- @ScottGriffiths:即使foo采用参数,docstring仍然是正确的。但是,help(foo)将显示_decorator的参数列表,因为它实际上取代了foo功能。如果你正在编写使用*args, **kwargs进行任意参数的修饰程序,那么没有好的方法可以解决这个问题,但是对于我来说,重要的一点是docstring保持完整。为了清晰起见,可以始终在docstring中指定参数详细信息。
- 谢谢你提供额外的信息。我最近一直未能获得装饰功能的帮助说明-这似乎是一个相当糟糕的情况,但我理解困难,因为装饰功能可能有一个完全不同的签名。不过,一定有办法…:)
- 有一种方法可以做到。decorator模块pypi.python.org/pypi/decorator使用一个技巧来完成它。技巧是重建装饰器签名并对其运行exec。你可以在decorator.py的第118行找到技巧。不过,我认为这种方法是极端的。
- 看来functools.wrapps确实使help()现在可以正常工作。我正在努力寻找这一点何时发生了变化,但我仍然在使用Python2.7。快乐的一天!
- 我该如何使用这个方法来处理博士学位论文@classmethod?
- 把像_decorator.__doc__ += '
(decorated by )'这样的线放在return _decorator前面怎么样?这似乎会使docstring更具信息性,但我没有看到这一点,所以想知道是否有理由不这样做。
我找到了一个解决方案,但不知道它是否真的很好:
1 2 3 4 5 6 7
| def decorator(f):
def _decorator():
print 'decorator active'
f()
_decorator.__name__=f.__name__
_decorator.__doc__=f.__doc__
return _decorator |
与_decorator.__name__=f.__name__的部分似乎有点可怕…你怎么认为?
- 事实上,这是(几乎?)确切地说,functools.wrapps的作用是:)
- 在我看来并不可怕。它准确地表达了你想要它说的话。"我希望此函数的名称是"myfunction",而不是"decorator"。
- 你不应该重新发明轮子,特别是当有一个标准库的工作功能,它可以做到这一点,经过良好的测试,维护和记录。
- 我不能和@azat ibrakov一起使用;我总是喜欢一个清晰易读的解决方案,它不依赖于任何(最终也会改变)库。因此,我真的喜欢这种简单而直接的方法(+1)。
看看functools.wraps:http://docs.python.org/library/functools.html