È possibile aggiungere una stringa di documentazione a una namedtuple in modo semplice?
Sì, in diversi modi.
Tipo di sottoclasse.NamedTuple - Python 3.6+
A partire da Python 3.6 possiamo usare una class
definizione typing.NamedTuple
direttamente con, con una docstring (e annotazioni!):
from typing import NamedTuple
class Card(NamedTuple):
"""This is a card type."""
suit: str
rank: str
Rispetto a Python 2, dichiarando vuoto __slots__
non è necessaria. In Python 3.8, non è necessario nemmeno per le sottoclassi.
Nota che la dichiarazione __slots__
non può essere non vuota!
In Python 3, puoi anche modificare facilmente il documento su una namedtuple:
NT = collections.namedtuple('NT', 'foo bar')
NT.__doc__ = """:param str foo: foo name
:param list bar: List of bars to bar"""
Ciò ci consente di visualizzare l'intento per loro quando chiamiamo aiuto su di loro:
Help on class NT in module __main__:
class NT(builtins.tuple)
| :param str foo: foo name
| :param list bar: List of bars to bar
...
Questo è davvero semplice rispetto alle difficoltà che abbiamo nel realizzare la stessa cosa in Python 2.
Python 2
In Python 2, dovrai
- sottoclasse la namedtuple e
- dichiarare
__slots__ == ()
La dichiarazione __slots__
è una parte importante che le altre risposte qui mancano .
Se non dichiari __slots__
, potresti aggiungere attributi ad-hoc modificabili alle istanze, introducendo bug.
class Foo(namedtuple('Foo', 'bar')):
"""no __slots__ = ()!!!"""
E adesso:
>>> f = Foo('bar')
>>> f.bar
'bar'
>>> f.baz = 'what?'
>>> f.__dict__
{'baz': 'what?'}
Ogni istanza creerà un separato __dict__
quando __dict__
si accede (la mancanza di __slots__
non impedirà altrimenti la funzionalità, ma la leggerezza della tupla, l'immutabilità e gli attributi dichiarati sono tutte caratteristiche importanti di namedtuples).
Avrai anche bisogno di un __repr__
, se vuoi che ciò che viene echeggiato sulla riga di comando ti dia un oggetto equivalente:
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__ = ()
una __repr__
come questo è necessario se si crea la namedtuple di base con un altro nome (come abbiamo fatto in precedenza con l'argomento stringa di nome, 'NTBase'
):
def __repr__(self):
return 'NT(foo={0}, bar={1})'.format(
repr(self.foo), repr(self.bar))
Per testare la replica, creare un'istanza, quindi verificare l'uguaglianza di un passaggio a eval(repr(instance))
nt = NT('foo', 'bar')
assert eval(repr(nt)) == nt
Esempio dalla documentazione
I documenti forniscono anche un esempio del genere, per quanto riguarda __slots__
: sto aggiungendo la mia docstring ad esso:
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)
...
La sottoclasse mostrata sopra imposta __slots__
una tupla vuota. Ciò aiuta a mantenere bassi i requisiti di memoria impedendo la creazione di dizionari di istanza.
Questo dimostra l'utilizzo sul posto (come suggerisce un'altra risposta qui), ma tieni presente che l'utilizzo sul posto potrebbe creare confusione quando guardi l'ordine di risoluzione del metodo, se stai eseguendo il debug, motivo per cui ho originariamente suggerito di usare Base
come suffisso per la base denominata coppia:
>>> Point.mro()
[<class '__main__.Point'>, <class '__main__.Point'>, <type 'tuple'>, <type 'object'>]
# ^^^^^---------------------^^^^^-- same names!
Per impedire la creazione di una __dict__
sottoclasse da una classe che la utilizza, è necessario dichiararla anche nella sottoclasse. Vedi anche questa risposta per ulteriori avvertenze sull'uso__slots__
.
namedtuple
in un vero e proprio "oggetto"? Perdendo così alcuni dei guadagni in termini di prestazioni dalle tuple con nome?