Czy można w łatwy sposób dodać ciąg dokumentacji do namedtuple?
Tak, na kilka sposobów.
Wpisywanie podklas NamedTuple - Python 3.6+
Od Pythona 3.6 możemy użyć class
definicji typing.NamedTuple
bezpośrednio, z ciągiem dokumentów (i adnotacjami!):
from typing import NamedTuple
class Card(NamedTuple):
"""This is a card type."""
suit: str
rank: str
W porównaniu z Pythonem 2 deklarowanie pustego elementu __slots__
nie jest konieczne. W Pythonie 3.8 nie jest to konieczne nawet w przypadku podklas.
Zwróć uwagę, że deklaracja __slots__
nie może być niepusta!
W Pythonie 3 możesz również łatwo zmienić dokument na nazwanym tulei:
NT = collections.namedtuple('NT', 'foo bar')
NT.__doc__ = """:param str foo: foo name
:param list bar: List of bars to bar"""
Co pozwala nam zobaczyć ich zamiary, gdy wzywamy ich do pomocy:
Help on class NT in module __main__:
class NT(builtins.tuple)
| :param str foo: foo name
| :param list bar: List of bars to bar
...
Jest to naprawdę proste w porównaniu z trudnościami, jakie napotykamy, wykonując to samo w Pythonie 2.
Python 2
W Pythonie 2 musisz
- podklasę namedtuple i
- ogłosić
__slots__ == ()
Deklarowanie __slots__
jest ważną częścią, której brakuje w innych odpowiedziach .
Jeśli nie zadeklarujesz __slots__
- możesz dodać zmienne atrybuty ad-hoc do instancji, wprowadzając błędy.
class Foo(namedtuple('Foo', 'bar')):
"""no __slots__ = ()!!!"""
I teraz:
>>> f = Foo('bar')
>>> f.bar
'bar'
>>> f.baz = 'what?'
>>> f.__dict__
{'baz': 'what?'}
Każda instancja utworzy oddzielną, __dict__
gdy __dict__
zostanie uzyskana (brak w __slots__
inny sposób nie utrudni funkcjonalności, ale lekkość krotki, niezmienność i zadeklarowane atrybuty są ważnymi cechami nazwanych krotek).
Będziesz także chciał a __repr__
, jeśli chcesz, aby to, co jest wyświetlane w linii poleceń, dawało ci równoważny obiekt:
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__ = ()
coś __repr__
takiego jest potrzebne, jeśli utworzysz bazę o nazwie tuple z inną nazwą (tak jak zrobiliśmy to powyżej z argumentem nazwa string, 'NTBase'
):
def __repr__(self):
return 'NT(foo={0}, bar={1})'.format(
repr(self.foo), repr(self.bar))
Aby przetestować repr, utwórz wystąpienie, a następnie przetestuj równość przejścia do eval(repr(instance))
nt = NT('foo', 'bar')
assert eval(repr(nt)) == nt
Przykład z dokumentacji
Dokumenty podają również taki przykład, dotyczący __slots__
- dodaję do niego własny dokument:
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)
...
Podklasa pokazana powyżej ustawia __slots__
pustą krotkę. Pomaga to utrzymać niskie wymagania dotyczące pamięci, zapobiegając tworzeniu słowników instancji.
Pokazuje to użycie w miejscu (jak sugeruje inna odpowiedź), ale zwróć uwagę, że użycie w miejscu może być mylące, gdy spojrzysz na kolejność rozwiązywania metod, jeśli debugujesz, dlatego pierwotnie sugerowałem użycie Base
jako sufiksu dla podstawy o nazwietuple:
>>> Point.mro()
[<class '__main__.Point'>, <class '__main__.Point'>, <type 'tuple'>, <type 'object'>]
# ^^^^^---------------------^^^^^-- same names!
Aby zapobiec tworzeniu __dict__
klasy podczas tworzenia podklasy z klasy, która jej używa, należy również zadeklarować ją w podklasie. Zobacz także tę odpowiedź, aby uzyskać więcej zastrzeżeń dotyczących używania__slots__
.
namedtuple
klasy w pełnoprawny „obiekt”? W ten sposób tracisz część zysków wydajnościowych z nazwanych krotek?