【问题标题】:Adding docstrings to namedtuples?将文档字符串添加到命名元组?
【发布时间】:2010-12-09 01:13:51
【问题描述】:

是否可以以简单的方式将文档字符串添加到命名元组?

我试过了

from collections import namedtuple

Point = namedtuple("Point", ["x", "y"])
"""
A point in 2D space
"""

# Yet another test

"""
A(nother) point in 2D space
"""
Point2 = namedtuple("Point2", ["x", "y"])

print Point.__doc__ # -> "Point(x, y)"
print Point2.__doc__ # -> "Point2(x, y)"

但这并没有削减它。有没有其他方法可以做?

【问题讨论】:

    标签: python docstring namedtuple


    【解决方案1】:

    不可以,你只能将文档字符串添加到模块、类和函数(包括方法)中

    【讨论】:

      【解决方案2】:

      您可以通过围绕来自namedtuple 的返回值创建一个简单的空包装类来实现此目的。我创建的文件的内容 (nt.py):

      from collections import namedtuple
      
      Point_ = namedtuple("Point", ["x", "y"])
      
      class Point(Point_):
          """ A point in 2d space """
          pass
      

      然后在 Python REPL 中:

      >>> print nt.Point.__doc__
       A point in 2d space 
      

      或者你可以这样做:

      >>> help(nt.Point)  # which outputs...
      
      关于模块 nt 中类 Point 的帮助: 类点(点) |二维空间中的一个点 | |方法解析顺序: |观点 |观点 | __builtin__.tuple | __builtin__.object ...

      如果您不喜欢每次都手动执行此操作,那么编写一种工厂函数来执行此操作很简单:

      def NamedTupleWithDocstring(docstring, *ntargs):
          nt = namedtuple(*ntargs)
          class NT(nt):
              __doc__ = docstring
          return NT
      
      Point3D = NamedTupleWithDocstring("A point in 3d space", "Point3d", ["x", "y", "z"])
      
      p3 = Point3D(1,2,3)
      
      print p3.__doc__
      

      哪个输出:

      A point in 3d space
      

      【讨论】:

      • 不会子类化将namedtuple 转换为成熟的“对象”吗?从而失去了命名元组带来的一些性能提升?
      • 如果将__slots__ = () 添加到派生子类中,您可以保留使用namedtuple 的内存和性能优势
      • 它仍然为 MRO 添加了另一个级别,这对于文档字符串来说是不合理的。但是,可以简单地分配给__doc__ 并在原始对象中保存一个自定义的文档字符串。
      【解决方案3】:

      您可以编写 Raymond Hettinger 的 namedtuple factory function 自己的版本,并添加可选的 docstring 参数。然而,使用与配方中相同的基本技术来定义您自己的工厂函数会更容易 - 并且可以说更好。无论哪种方式,您最终都会得到可重复使用的东西。

      from collections import namedtuple
      
      def my_namedtuple(typename, field_names, verbose=False,
                       rename=False, docstring=''):
          '''Returns a new subclass of namedtuple with the supplied
             docstring appended to the default one.
      
          >>> Point = my_namedtuple('Point', 'x, y', docstring='A point in 2D space')
          >>> print Point.__doc__
          Point(x, y):  A point in 2D space
          '''
          # create a base class and concatenate its docstring and the one passed
          _base = namedtuple(typename, field_names, verbose, rename)
          _docstring = ''.join([_base.__doc__, ':  ', docstring])
      
          # fill in template to create a no-op subclass with the combined docstring
          template = '''class subclass(_base):
              %(_docstring)r
              pass\n''' % locals()
      
          # execute code string in a temporary namespace
          namespace = dict(_base=_base, _docstring=_docstring)
          try:
              exec template in namespace
          except SyntaxError, e:
              raise SyntaxError(e.message + ':\n' + template)
      
          return namespace['subclass']  # subclass object created
      

      【讨论】:

        【解决方案4】:

        在想同样的事情时通过 Google 偶然发现了这个老问题。

        只是想指出,您可以通过直接从类声明中调用 namedtuple() 来进一步整理它:

        from collections import namedtuple
        
        class Point(namedtuple('Point', 'x y')):
            """Here is the docstring."""
        

        【讨论】:

        • 在课程中包含__slots__ = () 很重要。否则,你会为你的 attrs 创建一个__dict__,从而失去 namedtuple 的轻量级特性。
        【解决方案5】:

        在 Python 3 中,不需要包装器,因为类型的 __doc__ 属性是可写的。

        from collections import namedtuple
        
        Point = namedtuple('Point', 'x y')
        Point.__doc__ = '''\
        A 2-dimensional coordinate
        
        x - the abscissa
        y - the ordinate'''
        

        这与标准类定义密切相关,其中文档字符串跟在标题后面。

        class Point():
            '''A 2-dimensional coordinate
        
            x - the abscissa
            y - the ordinate'''
            <class code>
        

        这在 Python 2 中不起作用。

        AttributeError: attribute '__doc__' of 'type' objects is not writable

        【讨论】:

          【解决方案6】:

          无需使用已接受答案所建议的包装类。简单地添加一个文档字符串:

          from collections import namedtuple
          
          Point = namedtuple("Point", ["x", "y"])
          Point.__doc__="A point in 2D space"
          

          这会导致:(使用ipython3 的示例):

          In [1]: Point?
          Type:       type
          String Form:<class '__main__.Point'>
          Docstring:  A point in 2D space
          
          In [2]: 
          

          瞧!

          【讨论】:

          • 注意:这仅对 Python 3 有效。在 Python 2 中:AttributeError: attribute '__doc__' of 'type' objects is not writable
          【解决方案7】:

          是否可以以简单的方式将文档字符串添加到命名元组?

          是的,有多种方式。

          子类 typing.NamedTuple - Python 3.6+

          从 Python 3.6 开始,我们可以直接使用带有 typing.NamedTupleclass 定义,并带有文档字符串(和注释!):

          from typing import NamedTuple
          
          class Card(NamedTuple):
              """This is a card type."""
              suit: str
              rank: str
          

          与 Python 2 相比,不需要声明空的 __slots__。在 Python 3.8 中,即使是子类也不需要。

          注意声明__slots__不能为非空!

          在 Python 3 中,您还可以轻松地更改命名元组上的文档:

          NT = collections.namedtuple('NT', 'foo bar')
          
          NT.__doc__ = """:param str foo: foo name
          :param list bar: List of bars to bar"""
          

          当我们向他们寻求帮助时,这使我们能够查看他们的意图:

          Help on class NT in module __main__:
          
          class NT(builtins.tuple)
           |  :param str foo: foo name
           |  :param list bar: List of bars to bar
          ...
          

          与我们在 Python 2 中完成同样事情的困难相比,这真的很简单。

          Python 2

          在 Python 2 中,您需要

          • 继承命名元组,并且
          • 声明__slots__ == ()

          声明__slots__此处其他答案错过的重要部分

          如果您不声明 __slots__ - 您可以向实例添加可变的临时属性,从而引入错误。

          class Foo(namedtuple('Foo', 'bar')):
              """no __slots__ = ()!!!"""
          

          现在:

          >>> f = Foo('bar')
          >>> f.bar
          'bar'
          >>> f.baz = 'what?'
          >>> f.__dict__
          {'baz': 'what?'}
          

          每个实例在访问__dict__ 时都会创建一个单独的__dict__(缺少__slots__ 不会妨碍功能,但是元组的轻量性、不变性和声明的属性都是命名元组)。

          如果您希望在命令行上回显的内容为您提供等效对象,您还需要一个 __repr__

          NTBase = collections.namedtuple('NTBase', 'foo bar')
          
          class NT(NTBase):
              """
              Individual foo bar, a namedtuple
          
              :param str foo: foo name
              :param list bar: List of bars to bar
              """
              __slots__ = ()
          

          如果您创建具有不同名称的基本命名元组(就像我们在上面使用名称字符串参数 'NTBase' 所做的那样),则需要像这样的 __repr__

              def __repr__(self):
                  return 'NT(foo={0}, bar={1})'.format(
                          repr(self.foo), repr(self.bar))
          

          要测试 repr,实例化,然后测试传递到 eval(repr(instance)) 的相等性

          nt = NT('foo', 'bar')
          assert eval(repr(nt)) == nt
          

          文档中的示例

          docs also 给出了这样一个例子,关于__slots__ - 我正在向它添加我自己的文档字符串:

          class Point(namedtuple('Point', 'x y')):
              """Docstring added here, not in original"""
              __slots__ = ()
              @property
              def hypot(self):
                  return (self.x ** 2 + self.y ** 2) ** 0.5
              def __str__(self):
                  return 'Point: x=%6.3f  y=%6.3f  hypot=%6.3f' % (self.x, self.y, self.hypot)
          

          ...

          上面显示的子类将__slots__ 设置为一个空元组。这有助于 通过防止创建实例来保持较低的内存需求 字典。

          这演示了就地使用(就像这里的另一个答案所暗示的那样),但请注意,如果您正在调试,当您查看方法解析顺序时,就地使用可能会变得混乱,这就是我最初建议使用的原因Base 作为基础命名元组的后缀:

          >>> Point.mro()
          [<class '__main__.Point'>, <class '__main__.Point'>, <type 'tuple'>, <type 'object'>]
                          # ^^^^^---------------------^^^^^-- same names!        
          

          为了防止在从使用它的类继承时创建__dict__,您还必须在子类中声明它。另见this answer for more caveats on using __slots__

          【讨论】:

          • 虽然不如其他答案简洁明了,但这应该是公认的答案,因为它突出了__slots__ 的重要性。没有它,您将失去命名元组的轻量级价值。
          【解决方案8】:

          从 Python 3.5 开始,namedtuple 对象的文档字符串可以更新。

          来自whatsnew

          Point = namedtuple('Point', ['x', 'y'])
          Point.__doc__ += ': Cartesian coodinate'
          Point.x.__doc__ = 'abscissa'
          Point.y.__doc__ = 'ordinate'
          

          【讨论】:

            【解决方案9】:

            在 Python 3.6+ 中,您可以使用:

            class Point(NamedTuple):
                """
                A point in 2D space
                """
                x: float
                y: float
            

            【讨论】:

            【解决方案10】:

            我创建了这个函数来快速创建一个命名元组并记录元组及其每个参数:

            from collections import namedtuple
            
            
            def named_tuple(name, description='', **kwargs):
                """
                A named tuple with docstring documentation of each of its parameters
                :param str name: The named tuple's name
                :param str description: The named tuple's description
                :param kwargs: This named tuple's parameters' data with two different ways to describe said parameters. Format:
                    <pre>{
                        str: ( # The parameter's name
                            str, # The parameter's type
                            str # The parameter's description
                        ),
                        str: str, # The parameter's name: the parameter's description
                        ... # Any other parameters
                    }</pre>
                :return: collections.namedtuple
                """
                parameter_names = list(kwargs.keys())
            
                result = namedtuple(name, ' '.join(parameter_names))
            
                # If there are any parameters provided (such that this is not an empty named tuple)
                if len(parameter_names):
                    # Add line spacing before describing this named tuple's parameters
                    if description is not '':
                        description += "\n"
            
                    # Go through each parameter provided and add it to the named tuple's docstring description
                    for parameter_name in parameter_names:
                        parameter_data = kwargs[parameter_name]
            
                        # Determine whether parameter type is included along with the description or
                        # if only a description was provided
                        parameter_type = ''
                        if isinstance(parameter_data, str):
                            parameter_description = parameter_data
                        else:
                            parameter_type, parameter_description = parameter_data
            
                        description += "\n:param {type}{name}: {description}".format(
                            type=parameter_type + ' ' if parameter_type else '',
                            name=parameter_name,
                            description=parameter_description
                        )
            
                        # Change the docstring specific to this parameter
                        getattr(result, parameter_name).__doc__ = parameter_description
            
                # Set the docstring description for the resulting named tuple
                result.__doc__ = description
            
                return result
            

            然后您可以创建一个新的命名元组:

            MyTuple = named_tuple(
                "MyTuple",
                "My named tuple for x,y coordinates",
                x="The x value",
                y="The y value"
            )
            

            然后用你自己的数据实例化描述的命名元组,即。

            t = MyTuple(4, 8)
            print(t) # prints: MyTuple(x=4, y=8)
            

            通过python3命令行执行help(MyTuple)时显示如下:

            Help on class MyTuple:
            
            class MyTuple(builtins.tuple)
             |  MyTuple(x, y)
             |
             |  My named tuple for x,y coordinates
             |
             |  :param x: The x value
             |  :param y: The y value
             |
             |  Method resolution order:
             |      MyTuple
             |      builtins.tuple
             |      builtins.object
             |
             |  Methods defined here:
             |
             |  __getnewargs__(self)
             |      Return self as a plain tuple.  Used by copy and pickle.
             |
             |  __repr__(self)
             |      Return a nicely formatted representation string
             |
             |  _asdict(self)
             |      Return a new OrderedDict which maps field names to their values.
             |
             |  _replace(_self, **kwds)
             |      Return a new MyTuple object replacing specified fields with new values
             |
             |  ----------------------------------------------------------------------
             |  Class methods defined here:
             |
             |  _make(iterable) from builtins.type
             |      Make a new MyTuple object from a sequence or iterable
             |
             |  ----------------------------------------------------------------------
             |  Static methods defined here:
             |
             |  __new__(_cls, x, y)
             |      Create new instance of MyTuple(x, y)
             |
             |  ----------------------------------------------------------------------
             |  Data descriptors defined here:
             |
             |  x
             |      The x value
             |
             |  y
             |      The y value
             |
             |  ----------------------------------------------------------------------
             |  Data and other attributes defined here:
             |  
             |  _fields = ('x', 'y')
             |  
             |  _fields_defaults = {}
             |  
             |  ----------------------------------------------------------------------
             |  Methods inherited from builtins.tuple:
             |  
             |  __add__(self, value, /)
             |      Return self+value.
             |  
             |  __contains__(self, key, /)
             |      Return key in self.
             |  
             |  __eq__(self, value, /)
             |      Return self==value.
             |  
             |  __ge__(self, value, /)
             |      Return self>=value.
             |  
             |  __getattribute__(self, name, /)
             |      Return getattr(self, name).
             |  
             |  __getitem__(self, key, /)
             |      Return self[key].
             |  
             |  __gt__(self, value, /)
             |      Return self>value.
             |  
             |  __hash__(self, /)
             |      Return hash(self).
             |  
             |  __iter__(self, /)
             |      Implement iter(self).
             |  
             |  __le__(self, value, /)
             |      Return self<=value.
             |  
             |  __len__(self, /)
             |      Return len(self).
             |  
             |  __lt__(self, value, /)
             |      Return self<value.
             |  
             |  __mul__(self, value, /)
             |      Return self*value.
             |  
             |  __ne__(self, value, /)
             |      Return self!=value.
             |  
             |  __rmul__(self, value, /)
             |      Return value*self.
             |  
             |  count(self, value, /)
             |      Return number of occurrences of value.
             |  
             |  index(self, value, start=0, stop=9223372036854775807, /)
             |      Return first index of value.
             |      
             |      Raises ValueError if the value is not present.
            

            或者,您也可以通过以下方式指定参数的类型:

            MyTuple = named_tuple(
                "MyTuple",
                "My named tuple for x,y coordinates",
                x=("int", "The x value"),
                y=("int", "The y value")
            )
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2013-02-25
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多