Подтвердить что ты не робот

Добавление docstrings в namedtuples?

Можно ли легко добавить строку документации в namedtuple?

Я пробовал

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

но это не режет. Можно ли сделать другим способом?

4b9b3361

Ответ 1

Это можно сделать, создав простой, пустой класс оболочки вокруг возвращаемого значения из 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...
Help on class Point in module nt:

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

Если вам не нравится делать это вручную каждый раз, тривиально написать функцию вида factory для этого:

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

Ответ 2

Перешел через этот старый вопрос через Google, задаваясь тем же вопросом.

Просто хотел указать, что вы можете его убрать еще больше, вызвав namedtuple() прямо из объявления класса:

from collections import namedtuple

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

Ответ 3

В Python 3 оболочка не требуется, поскольку атрибуты __doc__ типов доступны для записи.

from collections import namedtuple

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

x - the abscissa
y - the ordinate'''

Это близко соответствует стандартным определениям класса, где docstring следует за заголовком.

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.

Ответ 4

Можно ли легко добавить строку документации в namedtuple?

Python 3

В Python 3 вы можете легко изменить документ на свой namedtuple:

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 вам нужно

  • подзаголовок namedtuple и
  • объявить __slots__ == ()

Объявление __slots__ - это важная часть, которую другие ответы здесь пропускают.

Если вы не объявляете __slots__ - вы можете добавить к экземплярам изменяемые атрибуты ad-hoc, введя ошибки.

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

И теперь:

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

Каждый экземпляр создаст отдельный __dict__, когда будет доступ к __dict__ (отсутствие __slots__ в противном случае не будет препятствовать функциональности, но легковесность кортежа, неизменность и объявленные атрибуты являются важными особенностями namedtuples).

Вам также понадобится __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__, как это необходимо, если вы создаете базовый namedtuple с другим именем (например, мы сделали это с аргументом string строки, 'NTBase'):

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

Чтобы проверить репрезентацию, создайте экземпляр, затем проверьте равенство прохода на eval(repr(instance))

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

Пример из документации

docs также дают такой пример, касающийся __slots__ - я добавляю к нему свою собственную docstring:

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 как суффикс для base namedtuple:

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

Чтобы предотвратить создание __dict__ при подклассу из класса, который его использует, вы также должны объявить его в подклассе. См. Также этот ответ для дополнительных предостережений при использовании __slots__.

Ответ 5

Так как Python 3.5, docstrings для объектов namedtuple могут быть обновлены.

Из whatsnew:

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

Ответ 6

Не нужно использовать класс оболочки, как это принято в принятом ответе. Просто буквально добавьте docstring:

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]: 

Вуаля!

Ответ 7

Вы можете составить собственную версию namedtuple factory функция Раймонда Хеттингера и добавить необязательный docstring.   Однако было бы проще - и, возможно, лучше - просто определить свою собственную функцию factory, используя ту же основную технику, что и в рецепте.   В любом случае, вы будете в конечном итоге с чем-то многоразовым.

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

Ответ 8

Нет, вы можете добавлять строки doc только к модулям, классам и функциям (включая методы)