Добавление 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)"
но это не режет. Можно ли сделать другим способом?
Ответы
Ответ 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 только к модулям, классам и функциям (включая методы)