【问题标题】:Python decorator handling docstringsPython 装饰器处理文档字符串
【发布时间】:2010-12-19 11:08:27
【问题描述】:

我在使用带有装饰器的文档字符串时遇到问题。举个例子:

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 的文档字符串,它显示:

Help on function _decorator in module __main__:

_decorator()

没有装饰器,帮助是正确的:

Help on function foo in module __main__:

foo()
    the magic foo function

我知道,函数foo 被装饰器包裹,所以函数对象不再是函数foo。但是,按预期获取文档字符串(和帮助)的好方法是什么?

【问题讨论】:

    标签: python decorator docstring


    【解决方案1】:

    使用functools.wraps()更新装饰器的属性:

    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,另请参阅Standard Library documentation

    【讨论】:

    • 如果 foo 接受任何参数,这将不起作用 - 它们会被 _decorator 使用的任何参数替换。这是一个问题,尤其是当您希望您的装饰器采用*args, **kwds 时。我从来没有找到使用functools.wraps 正确获取文档字符串的方法。
    • @Scott Griffiths:即使foo 接受参数,文档字符串仍然是正确的。但是,help(foo) 将显示_decorator 的参数列表,因为它实际上替换了foo 函数。如果您正在编写使用*args, **kwargs 接受任意参数的装饰器,则没有很好的解决方法,但对我来说,重要的一点是文档字符串保持完整。为清楚起见,始终可以在文档字符串中指定参数详细信息。
    • 感谢您提供额外信息。我最近一直未能为修饰函数获得正确的帮助描述 - 这似乎是一种非常糟糕的状态,但我理解其中的困难,因为修饰函数可能具有完全不同的签名。不过,一定有办法...... :)
    • 有办法做到这一点。装饰器模块pypi.python.org/pypi/decorator 使用了一个技巧。诀窍是重建装饰器签名并在其上运行 exec。你可以在 decorator.py 的第 118 行找到诀窍。但是我认为这种方法是极端的。
    • 看来 functools.wraps 确实 使help() 现在可以正常工作了。我正在努力寻找这种情况何时发生变化,但我仍在使用 Python 2.7。快乐的一天!
    【解决方案2】:

    找到了解决办法,不知道是不是真的好用:

    def decorator(f):
        def _decorator():
            print 'decorator active'
            f()
        _decorator.__name__=f.__name__
        _decorator.__doc__=f.__doc__
        return _decorator
    

    _decorator.__name__=f.__name__ 的部分看起来有点丑……你觉得呢?

    【讨论】:

    • 事实上这(几乎?)正是 functools.wraps 所做的 :)
    • 这对我来说并不可怕。它准确地表达了您想要它说的内容。 “我希望这个函数的名称是 'myfunction' 而不是 '_decorator'。”
    • 你不应该重新发明轮子,尤其是当标准库中有一个工作函数可以做到这一点时,经过充分测试、维护和记录
    • 我不能同意@Azat Ibrakov;我总是更喜欢一个清晰易读的解决方案,它不依赖于任何(最终也会改变)库。因此,我真的很喜欢这种简单直接的方法 (+1)。
    • 此外,此解决方案不会复制 f 参数的文档字符串(如果有的话)和返回类型。 functools.wraps 会这样做。
    【解决方案3】:

    【讨论】:

      【解决方案4】:

      解决方案非常简单。文档字符串应该在主函数顶部调用的最顶层装饰器中提及。查找以下示例:

      import math
      
      def wrap2(func):
          def squarer(x):
              return [math.sqrt(i) for i in func(x)]
          return squarer
          
      
      def wrap1(func):
          def summer(x):
              return [i*2 for i in func(x)]
          return summer
      
      def wrap3(func):
          def rounder(x):
              return [round(i,1) for i in func(x)]
          return rounder
      
      def wrap4(func):
          def stringer(x):
              '''
          Enter the input of a 2-dim array to get the output
          '''
              return [str(i)+' rounds ' for i in func(x)]
          return stringer
      
      @wrap4
      @wrap3
      @wrap2
      @wrap1
      def rooter(i):
          return [sum(p) for p in i]
      
      help(rooter)
      
      # output
      Help on function stringer in module __main__:
      
      stringer(x)
          Enter the input of a 2-dim array to get the output
      
      

      ---或者----

      Signature: rooter(x)
      Docstring: Enter the input of a 2-dim array to get the output
      File:      d:\<ipython-input-392-487fc73b05cf>
      Type:      function
      
      

      因此,必须在 wrap4 装饰器函数中提及 Doc String 才能在主函数中可见。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-03-17
        • 2023-03-18
        • 2020-06-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多