Comment documenter les attributs de classe en Python? [fermé]


115

J'écris une classe légère dont les attributs sont destinés à être accessibles au public, et parfois seulement remplacés dans des instanciations spécifiques. Il n'y a aucune disposition dans le langage Python pour créer des docstrings pour les attributs de classe, ou toute sorte d'attributs, d'ailleurs. Quelle est la manière attendue et prise en charge, devrait-il y en avoir une, pour documenter ces attributs? Actuellement, je fais ce genre de chose:

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)

Cela se traduira par la docstring de la classe contenant la section docstring standard initiale, ainsi que les lignes ajoutées pour chaque attribut via une affectation augmentée à __doc__.

Bien que ce style ne semble pas être expressément interdit dans les directives de style docstring , il n'est pas non plus mentionné comme une option. L'avantage ici est qu'il fournit un moyen de documenter les attributs avec leurs définitions, tout en créant une docstring de classe présentable, et en évitant d'avoir à écrire des commentaires qui réitèrent les informations de la docstring. Je suis toujours un peu ennuyé de devoir écrire les attributs deux fois; J'envisage d'utiliser les représentations sous forme de chaîne des valeurs dans la docstring pour au moins éviter la duplication des valeurs par défaut.

Est-ce une violation odieuse des conventions communautaires ad hoc? Ça va? Y a-t-il un meilleur moyen? Par exemple, il est possible de créer un dictionnaire contenant des valeurs et des docstrings pour les attributs, puis d'ajouter le contenu à la classe __dict__et à la docstring vers la fin de la déclaration de classe; cela éviterait de devoir taper deux fois les noms et valeurs d'attribut. edit : cette dernière idée n'est, je pense, pas réellement possible, du moins pas sans construire dynamiquement toute la classe à partir de données, ce qui semble être une très mauvaise idée à moins qu'il n'y ait une autre raison de le faire.

Je suis assez nouveau en python et je travaille toujours sur les détails du style de codage, donc les critiques sans rapport sont également les bienvenues.


Si vous cherchez un moyen de documenter les attributs du modèle Django, cela peut être utile: djangosnippets.org/snippets/2533
Michael Scheper

3
Dupliquer de Comment documenter les champs et les propriétés en Python? qui détiennent une solution différente.
bufh

1
Je ne comprends pas pourquoi c'est basé sur l'opinion. Python documente spécifiquement ses conventions acceptables dans les PEP. Il existe différents outils sources Python qui extraient une documentation correctement formatée. En fait, Python a en fait un attribute doc stringmentionné dans PEP 257 qui n'est pas bien connu et semble difficile à trouver qui peut répondre à la question des OP, et est pris en charge par certains outils sources. Ce n'est pas une opinion. C'est un fait, et une partie du langage, et à peu près exactement ce que veut l'OP.
NeilG

Réponses:


83

Pour éviter toute confusion: le terme propriété a une signification spécifique en python. Ce dont vous parlez, c'est ce que nous appelons les attributs de classe . Puisqu'ils sont toujours utilisés à travers leur classe, je trouve qu'il est logique de les documenter dans la doc string de la classe. Quelque chose comme ça:

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"

Je pense que c'est beaucoup plus facile pour les yeux que l'approche de votre exemple. Si je voulais vraiment qu'une copie des valeurs d'attribut apparaisse dans la chaîne doc, je les placerais à côté ou en dessous de la description de chaque attribut.

Gardez à l'esprit qu'en Python, les chaînes doc sont des membres réels des objets qu'elles documentent, pas simplement des annotations de code source. Puisque les variables d'attribut de classe ne sont pas des objets eux-mêmes mais des références à des objets, elles n'ont aucun moyen de contenir leurs propres chaînes de documents. Je suppose que vous pourriez faire un cas pour les chaînes doc sur les références, peut-être pour décrire "ce qui devrait aller ici" au lieu de "ce qui est réellement ici", mais je trouve assez facile de le faire dans la chaîne doc de la classe contenant.


Je suppose que dans la plupart des cas, c'est bien, car les attributs - merci pour la correction de la terminologie - sont assez succinctement déclarés pour qu'ils puissent simplement être regroupés au début de la déclaration de classe sans qu'il soit impossible de basculer dans les deux sens {lire les deux la documentation et la valeur par défaut} ou {mettre à jour les deux instances de la documentation et / ou la valeur par défaut}.
intuité le

1
Notez également que mon exemple fera apparaître la documentation des attributs dans la docstring de la classe. En fait, je préférerais mettre la documentation dans les docstrings des attributs eux-mêmes, mais cela ne fonctionne pas pour la plupart des types intégrés.
intuité le

Oui, mon idée initiale était de simplement déclarer par exemple flight_speed = 691; flight_speed.__doc__ = "blah blah". Je pense que c'est ce que vous mentionnez dans votre modification . Malheureusement, cela ne fonctionne pas pour les instanciations de (la plupart?) Types intégrés (comme intdans cet exemple). Cela fonctionne pour les instanciations de types définis par l'utilisateur. =========== Il y avait en fait un PEP (désolé, oubliez le numéro) qui proposait d'ajouter des docstrings pour les attributs de classe / module, mais il a été refusé car ils ne pouvaient pas trouver un moyen de le clarifier si les docstrings étaient pour les attributs précédents ou suivants.
intuitu le

2
Et si ce sont des attributs d'instance? document encore dans la classe docstring ou quoi?
n611x007

1
@intuited Était-ce ce PEP? legacy.python.org/dev/peps/pep-0224
taz

30

Vous citez le PEP257: Docstring Conventions, dans la section Qu'est - ce qu'une docstring, il est indiqué:

Les chaînes littérales apparaissant ailleurs dans le code Python peuvent également servir de documentation. Ils ne sont pas reconnus par le compilateur bytecode Python et ne sont pas accessibles en tant qu'attributs d'objet d'exécution (c'est-à-dire non assignés à __doc__), mais deux types de docstrings supplémentaires peuvent être extraits par des outils logiciels:

Les littéraux de chaîne apparaissant immédiatement après une simple affectation au niveau supérieur d'un module, d'une classe ou d'une méthode __init__ sont appelés "docstrings d'attribut".

Et ceci est expliqué plus en détail dans PEP 258: docstrings d'attribut. Comme expliqué ci-dessus ʇsәɹoɈ. un attribut n'est pas un objet qui peut posséder un __doc__ donc il n'apparaîtra pas dans help()ou pydoc. Ces docstrings ne peuvent être utilisées que pour la documentation générée.

Ils sont utilisés dans Sphinx avec la directive autoattribute

Sphinx peut utiliser des commentaires sur une ligne avant une affectation ou un commentaire spécial après une affectation ou une docstring après la définition qui sera autodocumentée.


1
Le plugin jedi-vim reconnaît également les docstrings d'attributs.
Long Vu

1
Je ne sais pas quand cela a été introduit, mais Sphinx 1.2.2 semble inclure des docstrings d'attributs dans la documentation générée.
jochen

1
Merci @jochen, je mets à jour ma réponse.
marcz

3
Veuillez noter que la PEP 258 est rejetée. L'avis de rejet déclare: "Bien que cela puisse servir de document de conception intéressant pour les docutils désormais indépendants, il n'est plus prévu pour l'inclusion dans la bibliothèque standard."
Michał Łazowik

13

Vous pourriez abuser des propriétés à cet effet. Les propriétés contiennent un getter, un setter, un deleter et une docstring . Naïvement, cela deviendrait très verbeux:

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

Ensuite, vous aurez une docstring appartenant à Cx:

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

Faire cela pour de nombreux attributs est fastidieux, mais vous pouvez envisager une fonction d'assistance 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!

Ensuite, appeler Pythons interactif helpdonnera:

Help on class C in module __main__:

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

ce qui, je pense, devrait être à peu près ce que vous recherchez.

Edit : Je me rends compte maintenant que nous pouvons peut-être éviter de devoir passer le premier argument myprop, car le nom interne n'a pas d'importance. Si les appels ultérieurs de myproppeuvent d'une manière ou d'une autre communiquer entre eux, il pourrait automatiquement décider d'un nom d'attribut interne long et improbable. Je suis sûr qu'il existe des moyens de mettre en œuvre cela, mais je ne suis pas sûr qu'ils en valent la peine.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.