Come documentare gli attributi di classe in Python? [chiuso]


115

Sto scrivendo una classe leggera i cui attributi devono essere accessibili pubblicamente e solo a volte sovrascritti in istanze specifiche. Non esiste alcuna disposizione nel linguaggio Python per la creazione di docstring per attributi di classe, o qualsiasi tipo di attributo, per quella materia. Qual è il modo previsto e supportato, dovrebbe essercene uno, per documentare questi attributi? Attualmente sto facendo questo genere di cose:

class Albatross(object):
    """A bird with a flight speed exceeding that of an unladen swallow.

    Attributes:
    """

    flight_speed = 691
    __doc__ += """
        flight_speed (691)
          The maximum speed that such a bird can attain.
    """

    nesting_grounds = "Raymond Luxury-Yacht"
    __doc__ += """
        nesting_grounds ("Raymond Luxury-Yacht")
          The locale where these birds congregate to reproduce.
    """

    def __init__(self, **keyargs):
        """Initialize the Albatross from the keyword arguments."""
        self.__dict__.update(keyargs)

Ciò risulterà nella docstring della classe contenente la sezione docstring standard iniziale, così come le righe aggiunte per ogni attributo tramite l'assegnazione aumentata a __doc__.

Sebbene questo stile non sembri essere espressamente vietato nelle linee guida dello stile della docstring , non è nemmeno menzionato come opzione. Il vantaggio qui è che fornisce un modo per documentare gli attributi insieme alle loro definizioni, creando comunque una docstring di classe presentabile ed evitando di dover scrivere commenti che reiterano le informazioni dalla docstring. Sono ancora un po 'infastidito dal fatto che devo effettivamente scrivere due volte gli attributi; Sto considerando di utilizzare le rappresentazioni di stringa dei valori nella docstring per evitare almeno la duplicazione dei valori predefiniti.

È questa una atroce violazione delle convenzioni comunitarie ad hoc? Va bene? C'è un modo migliore? Ad esempio, è possibile creare un dizionario contenente valori e docstring per gli attributi e quindi aggiungere il contenuto alla classe __dict__e docstring verso la fine della dichiarazione di classe; questo allevierebbe la necessità di digitare due volte i nomi e i valori degli attributi. modifica : quest'ultima idea è, credo, non effettivamente possibile, almeno non senza costruire dinamicamente l'intera classe dai dati, il che sembra davvero una pessima idea a meno che non ci sia qualche altra ragione per farlo.

Sono abbastanza nuovo in Python e sto ancora elaborando i dettagli dello stile di codifica, quindi anche le critiche non correlate sono benvenute.


Se stai cercando un modo per documentare gli attributi del modello Django, questo potrebbe essere utile: djangosnippets.org/snippets/2533
Michael Scheper

3
Duplicato di Come documentare campi e proprietà in Python? che contengono una soluzione diversa.
bufh

1
Non capisco perché questo sia basato sull'opinione. Python documenta specificamente le sue convenzioni accettabili nei PEP. Esistono diversi strumenti sorgente Python che estraggono la documentazione formattata correttamente. In effetti Python ha effettivamente un attribute doc stringmenzionato in PEP 257 che non è ben noto e sembra difficile da trovare che possa rispondere alla domanda degli OP, ed è supportato da alcuni strumenti di origine. Questa non è un'opinione. È un fatto, e parte del linguaggio, e più o meno esattamente ciò che vuole l'OP.
NeilG

Risposte:


83

Per evitare confusione: il termine proprietà ha un significato specifico in python. Quello di cui stai parlando è ciò che chiamiamo attributi di classe . Dal momento che vengono sempre seguiti attraverso la loro classe, trovo che abbia senso documentarli nella doc string della classe. Qualcosa come questo:

class Albatross(object):
    """A bird with a flight speed exceeding that of an unladen swallow.

    Attributes:
        flight_speed     The maximum speed that such a bird can attain.
        nesting_grounds  The locale where these birds congregate to reproduce.
    """
    flight_speed = 691
    nesting_grounds = "Throatwarbler Man Grove"

Penso che sia molto più facile per gli occhi rispetto all'approccio nel tuo esempio. Se davvero volessi che una copia dei valori degli attributi appaia nella doc string, li metterei accanto o sotto la descrizione di ogni attributo.

Tieni presente che in Python, le stringhe di documento sono membri effettivi degli oggetti che documentano, non semplicemente annotazioni del codice sorgente. Poiché le variabili degli attributi di classe non sono oggetti in sé ma riferimenti a oggetti, non hanno modo di conservare le stringhe di documentazione proprie. Immagino che potresti sostenere le stringhe di documentazione sui riferimenti, forse per descrivere "cosa dovrebbe andare qui" invece di "cosa è effettivamente qui", ma trovo che sia abbastanza facile farlo nella doc string della classe contenente.


Immagino che nella maggior parte dei casi questo vada bene, dal momento che gli attributi —grazie per la correzione della terminologia— sono dichiarati in modo abbastanza succinto da poter essere raggruppati all'inizio della dichiarazione di classe senza rendere impraticabile andare avanti e indietro per {leggere entrambi la documentazione e il valore predefinito} o {aggiornare entrambe le istanze della documentazione e / o il valore predefinito}.
intuito il

1
Si noti inoltre che il mio esempio farà apparire la documentazione per gli attributi nella docstring della classe. In realtà preferirei mettere la documentazione in docstrings degli attributi stessi, ma questo non funziona per la maggior parte dei tipi incorporati.
intuito il

Sì, la mia idea iniziale era di dichiarare ad es flight_speed = 691; flight_speed.__doc__ = "blah blah". Penso che questo sia ciò che stai menzionando nella tua modifica . Sfortunatamente, questo non funziona per le istanze di (la maggior parte?) Tipi incorporati (come intin quell'esempio). Funziona per istanze di tipi definiti dall'utente. =========== In realtà c'era un PEP (scusa, dimentica il numero) che proponeva di aggiungere docstrings per attributi di classe / modulo, ma è stato rifiutato perché non riuscivano a trovare un modo per renderlo chiaro se le docstring erano per gli attributi precedenti o seguenti.
intuito l'

2
quindi cosa succede se sono attributi di istanza? ancora documento nella classe docstring o cosa?
n611x007

1
@intuited Era questo PEP? legacy.python.org/dev/peps/pep-0224
taz

30

Citi le PEP257: Convenzioni Docstring, nella sezione Cos'è una docstring si afferma:

Le stringhe letterali che si trovano altrove nel codice Python possono anche fungere da documentazione. Non sono riconosciuti dal compilatore bytecode Python e non sono accessibili come attributi oggetto di runtime (cioè non assegnati a __doc__), ma due tipi di docstrings extra possono essere estratti dagli strumenti software:

I valori letterali stringa che si verificano immediatamente dopo una semplice assegnazione al livello superiore di un modulo, una classe o un metodo __init__ sono chiamati "attributi docstrings".

E questo è spiegato in maggiore dettaglio in PEP 258: Attribute docstrings. Come spiega sopra ʇsәɹoɈ. un attributo non è un oggetto che può possedere un __doc__ quindi non appariranno in help()o pydoc. Queste docstring possono essere utilizzate solo per la documentazione generata.

Sono usati in Sphinx con la direttiva autoattribute

Sphinx può usare commenti su una riga prima di un compito o un commento speciale dopo un compito o una docstring dopo la definizione che sarà documentata automaticamente.


1
Il plugin jedi-vim riconosce anche le docstring degli attributi.
Long Vu

1
Non so quando sia stato introdotto, ma Sphinx 1.2.2 sembra includere le docstring degli attributi nella documentazione generata.
jochen

1
Grazie @jochen, aggiorno la mia risposta.
marcz

3
Si prega di notare che PEP 258 è rifiutato. L'avviso di rifiuto afferma: "Anche se questo può servire come un interessante documento di progettazione per i docutil ora indipendenti, non è più previsto per l'inclusione nella libreria standard."
Michał Łazowik

13

Potresti abusare delle proprietà in questo senso. Le proprietà contengono un getter, un setter, un deleter e una docstring . Ingenuamente, questo diventerebbe molto prolisso:

class C:
    def __init__(self):
        self._x = None

    @property
    def x(self):
        """Docstring goes here."""
        return self._x

    @x.setter
    def x(self, value):
        self._x = value

    @x.deleter
    def x(self):
        del self._x

Quindi avrai una docstring appartenente a Cx:

In [24]: print(C.x.__doc__)
Docstring goes here.

Fare questo per molti attributi è complicato, ma potresti immaginare una funzione di aiuto myprop:

def myprop(x, doc):
    def getx(self):
        return getattr(self, '_' + x)

    def setx(self, val):
        setattr(self, '_' + x, val)

    def delx(self):
        delattr(self, '_' + x)

    return property(getx, setx, delx, doc)

class C:
    a = myprop("a", "Hi, I'm A!")
    b = myprop("b", "Hi, I'm B!")

In [44]: c = C()

In [46]: c.b = 42

In [47]: c.b
Out[47]: 42

In [49]: print(C.b.__doc__)
Hi, I'm B!

Quindi, la chiamata interattiva di Pythons helpdarà:

Help on class C in module __main__:

class C
 |  Data descriptors defined here:
 |  
 |  a
 |      Hi, I'm A!
 |  
 |  b
 |      Hi, I'm B!

che penso dovrebbe essere più o meno quello che stai cercando.

Modifica : mi rendo conto ora che forse possiamo evitare di dover passare il primo argomento a myprop, perché il nome interno non ha importanza. Se le chiamate successive di myproppossono in qualche modo comunicare tra loro, potrebbe decidere automaticamente un nome di attributo interno lungo e improbabile. Sono sicuro che ci sono modi per implementarlo, ma non sono sicuro che ne valga la pena.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.