python 将文档字符串添加到命名元组?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1606436/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-03 22:41:21  来源:igfitidea点击:

Adding docstrings to namedtuples?

pythondocstringnamedtuple

提问by Rickard

Is it possible to add a documentation string to a namedtuple in an easy manner?

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

I tried

我试过

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)"

but that doesn't cut it. Is it possible to do in some other way?

但这并不能解决问题。是否有可能以其他方式进行?

采纳答案by Mark Rushakoff

You can achieve this by creating a simple, empty wrapper class around the returned value from namedtuple. Contents of a file I created (nt.py):

您可以通过围绕从namedtuple. 我创建的文件内容 ( nt.py):

from collections import namedtuple

Point_ = namedtuple("Point", ["x", "y"])

class Point(Point_):
    """ A point in 2d space """
    pass

Then in the Python REPL:

然后在 Python REPL 中:

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

Or you could do:

或者你可以这样做:

>>> help(nt.Point)  # which outputs...
Help on class Point in module nt:

class Point(Point)
 |  A point in 2d space
 |  
 |  Method resolution order:
 |      Point
 |      Point
 |      __builtin__.tuple
 |      __builtin__.object
 ...

If you don't like doing that by hand every time, it's trivial to write a sort-of factory function to do this:

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

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__

which outputs:

输出:

A point in 3d space

回答by Terry Jan Reedy

In Python 3, no wrapper is needed, as the __doc__attributes of types is writable.

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

from collections import namedtuple

Point = namedtuple('Point', 'x y')
Point.__doc__ = '''\
A 2-dimensional coordinate

x - the abscissa
y - the ordinate'''

This closely corresponds to a standard class definition, where the docstring follows the header.

这与标准类定义非常相似,其中文档字符串跟在标题之后。

class Point():
    '''A 2-dimensional coordinate

    x - the abscissa
    y - the ordinate'''
    <class code>

This does not work in Python 2.

这在 Python 2 中不起作用。

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

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

回答by CoupleWavyLines

Came across this old question via Google while wondering the same thing.

通过谷歌遇到这个老问题,同时想知道同样的事情。

Just wanted to point out that you can tidy it up even more by calling namedtuple() right from the class declaration:

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

from collections import namedtuple

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

回答by Aaron Hall

Is it possible to add a documentation string to a namedtuple in an easy manner?

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

Yes, in several ways.

是的,在几个方面。

Subclass typing.NamedTuple - Python 3.6+

子类打字.NamedTuple - Python 3.6+

As of Python 3.6 we can use a classdefinition with typing.NamedTupledirectly, with a docstring (and annotations!):

对于Python 3.6的,我们可以用一个class定义与typing.NamedTuple直接与文档字符串(和注解!):

from typing import NamedTuple

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

Compared to Python 2, declaring empty __slots__is not necessary. In Python 3.8, it isn't necessary even for subclasses.

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

Note that declaring __slots__cannot be non-empty!

请注意,声明__slots__不能为非空!

In Python 3, you can also easily alter the doc on a namedtuple:

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

NT = collections.namedtuple('NT', 'foo bar')

NT.__doc__ = """:param str foo: foo name
:param list bar: List of bars to bar"""

Which allows us to view the intent for them when we call help on them:

这使我们可以在对他们寻求帮助时查看他们的意图:

Help on class NT in module __main__:

class NT(builtins.tuple)
 |  :param str foo: foo name
 |  :param list bar: List of bars to bar
...

This is really straightforward compared to the difficulties we have accomplishing the same thing in Python 2.

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

Python 2

蟒蛇2

In Python 2, you'll need to

在 Python 2 中,你需要

  • subclass the namedtuple, and
  • declare __slots__ == ()
  • 子类化命名元组,以及
  • 宣布 __slots__ == ()

Declaring __slots__is an important part that the other answers here miss.

声明__slots__这里的其他答案错过的重要部分

If you don't declare __slots__- you could add mutable ad-hoc attributes to the instances, introducing bugs.

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

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

And now:

现在:

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

Each instance will create a separate __dict__when __dict__is accessed (the lack of __slots__won't otherwise impede the functionality, but the lightweightness of the tuple, immutability, and declared attributes are all important features of namedtuples).

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

You'll also want a __repr__, if you want what is echoed on the command line to give you an equivalent object:

您还需要一个__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__ = ()

a __repr__like this is needed if you create the base namedtuple with a different name (like we did above with the name string argument, 'NTBase'):

__repr__,如果你创建一个不同的名称基本namedtuple(就像我们的名称字符串参数,上面不喜欢,这是需要'NTBase'):

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

To test the repr, instantiate, then test for equality of a pass to eval(repr(instance))

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

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

Example from the documentation

文档中的示例

The docs alsogive such an example, regarding __slots__- I'm adding my own docstring to it:

文档还给出这样一个例子,关于__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)

...

The subclass shown above sets __slots__to an empty tuple. This helps keep memory requirements low by preventing the creation of instance dictionaries.

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__为一个空元组。这有助于通过防止创建实例字典来保持较低的内存要求。

This demonstrates in-place usage (like another answer here suggests), but note that the in-place usage may become confusing when you look at the method resolution order, if you're debugging, which is why I originally suggested using Baseas a suffix for the base namedtuple:

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

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

To prevent creation of a __dict__when subclassing from a class that uses it, you must also declare it in the subclass. See also this answer for more caveats on using __slots__.

为了防止__dict__从使用它的类继承时创建 a ,您还必须在子类中声明它。有关使用__slots__.

回答by vishes_shell

Since Python 3.5, docstrings for namedtupleobjects can be updated.

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

From the whatsnew:

whatsnew

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

回答by Docent

In Python 3.6+ you can use:

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

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

回答by A Sz

No need to use a wrapper class as suggested by the accepted answer. Simply literally adda docstring:

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

from collections import namedtuple

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

This results in: (example using ipython3):

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

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

In [2]: 

Voilà!

瞧!

回答by martineau

You could concoct your own version of the namedtuple factory functionby Raymond Hettinger and add an optional docstringargument.  However it would be easier -- and arguably better -- to just define your own factory function using the same basic technique as in the recipe.  Either way, you'll end up with something reusable.

您可以编造自己版本的Raymond Hettinger的namedtuple 工厂函数并添加一个可选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

回答by Steven

I created this function to quickly create a named tuple and document the tuple along with each of its parameters:

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

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

You can then create a new named tuple:

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

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

Then instantiate the described named tuple with your own data, ie.

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

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

When executing help(MyTuple)via the python3 command line the following is shown:

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

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.

Alternatively, you can also specify the parameter's type via:

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

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

回答by Jeffrey Aylesworth

No, you can only add doc strings to modules, classes and function (including methods)

不,您只能向模块、类和函数(包括方法)添加文档字符串