Чи можна легко додати рядок документації до іменованого парню просто?
Так, кількома способами.
Набір підкласу. NameedTuple - 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 ви також можете легко змінити документ у вказаному наборі:
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__
- ви можете додати змінні спеціальні атрибути до екземплярів, вносячи помилки.
class Foo(namedtuple('Foo', 'bar')):
"""no __slots__ = ()!!!"""
І зараз:
>>> f = Foo('bar')
>>> f.bar
'bar'
>>> f.baz = 'what?'
>>> f.__dict__
{'baz': 'what?'}
Кожен екземпляр створить окремий __dict__
при __dict__
доступі (відсутність __slots__
не по-іншому перешкоджатиме функціональності, але легкість кортежу, незмінюваність та оголошені атрибути - це всі важливі особливості іменних кортежів).
Вам також знадобиться a __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
як суфікс для основи з назвою:
>>> Point.mro()
[<class '__main__.Point'>, <class '__main__.Point'>, <type 'tuple'>, <type 'object'>]
# ^^^^^---------------------^^^^^-- same names!
Щоб запобігти створенню а __dict__
підклассу з класу, який його використовує, ви також повинні оголосити його в підкласі. Дивіться також цю відповідь, щоб отримати додаткові застереження щодо використання__slots__
.
namedtuple
на повноцінний "об'єкт"? Тим самим втрачаючи частину прибутків від іменних кортежів?