web-dev-qa-db-fra.com

Comment documenter les attributs de classe en Python?

J'écris une classe légère dont les attributs sont destinés à être accessibles au public et ne sont parfois remplacés que 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 n'importe quelle sorte d'attributs, d'ailleurs. Quelle est la manière acceptée, le cas échéant, de 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 de 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 option. L'avantage ici est qu'il fournit un moyen de documenter les attributs à côté de 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 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 une meilleure façon? 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 docstring vers la fin de la déclaration de classe; cela allégerait la nécessité de taper deux fois les noms et les valeurs d'attribut. edit : cette dernière idée est, je pense, pas vraiment possible, du moins pas sans construire dynamiquement toute la classe à partir de données, ce qui semble vraiment mauvais idée à moins qu'il n'y ait une autre raison de le faire.

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

97
intuited

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 traités par leur classe, je trouve qu'il est logique de les documenter dans la chaîne de doc 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 dans votre exemple. Si je voulais vraiment qu'une copie des valeurs d'attribut apparaisse dans la chaîne doc, je les mettrais à côté ou en dessous de la description de chaque attribut.

Gardez à l'esprit qu'en Python, les chaînes de doc sont des membres réels des objets qu'elles documentent, et pas seulement des annotations de code source. Étant donné que 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 conserver leurs propres chaînes de doc. Je suppose que vous pourriez justifier les chaînes de 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 cela assez facile à faire dans la chaîne de doc de classe contenant.

72
ʇsәɹoɈ

Vous citez le PEP257: Conventions Docstring, dans la section Qu'est-ce qu'un docstring il est dit:

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

Les littéraux de chaîne se produisant 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 "chaînes de documents d'attribut".

Et cela est expliqué plus en détail dans PEP 258: Docstrings d'attributs. Comme expliqué ci-dessus ʇsәɹoɈ. un attribut n'est pas un objet qui peut posséder un __doc__ donc ils n'apparaîtront 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.

26
marcz

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 à C.x:

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

Faire cela pour de nombreux attributs est lourd, mais vous pouvez imaginer une fonction d'aide 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 help donnera:

Help on class C in module __main__:

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

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

Edit: Je me rends compte maintenant que nous pouvons peut-être éviter d'avoir à passer le premier argument à myprop, car le nom interne n'a pas d'importance. Si les appels ultérieurs de myprop peuvent communiquer d'une manière ou d'une autre, 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 sais pas si cela en vaut la peine.

11
gerrit