Можно ли простым способом добавить строку документации в namedtuple?
Да, несколькими способами.
Типирование подкласса.NamedTuple — Python 3.6+
Начиная с Python 3.6 мы можем использовать определение class
напрямую с typing.NamedTuple
, со строкой документации (и аннотациями!):
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 вы также можете легко изменить документ для 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.
Питон 2
В Python 2 вам нужно
- подкласс namedtuple, и
- объявить
__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__
в противном случае не будет препятствовать функциональности, но легкость кортежа, неизменность и объявленные атрибуты — все это важные особенности 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__ = ()
такой __repr__
необходим, если вы создаете базовый namedtuple с другим именем (как мы сделали выше с аргументом строки имени, '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
Пример из документации
документы также приведите такой пример относительно __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
в качестве суффикс для основания namedtuple:
>>> Point.mro()
[<class '__main__.Point'>, <class '__main__.Point'>, <type 'tuple'>, <type 'object'>]
# ^^^^^---------------------^^^^^-- same names!
Чтобы предотвратить создание __dict__
при создании подкласса из класса, который его использует, вы также должны объявить его в подклассе. См. также этот ответ, чтобы узнать больше об использовании __slots__
.
person
Aaron Hall
schedule
17.02.2015