112 votes

Hériter des docstrings dans l'héritage des classes Python

J'essaie de faire de l'héritage de classe en Python. J'aimerais que chaque classe et classe héritée ait une bonne documentation. Donc je pense que pour la classe héritée, je voudrais qu'elle.. :

  • hériter de la docstring de la classe de base
  • peut-être ajouter une documentation supplémentaire pertinente à la docstring

Existe-t-il un moyen (éventuellement élégant ou pythonique) de faire ce genre de manipulation de la docstring dans une situation d'héritage de classe ? Et pour l'héritage multiple ?

3 votes

Je ne peux pas répondre car la question a malheureusement été fermée, mais à partir de Python 3.5, inspect.getdoc cherchera dans l'arbre d'héritage jusqu'à ce qu'il trouve une docstring.

1 votes

48voto

John Feminella Points 116878

Vous n'êtes pas le seul ! Il y a eu une discussion sur comp.lang.python à ce sujet il y a quelque temps, et une recette a été créée. Consultez-la aquí .

"""
doc_inherit decorator

Usage:

class Foo(object):
    def foo(self):
        "Frobber"
        pass

class Bar(Foo):
    @doc_inherit
    def foo(self):
        pass 

Now, Bar.foo.__doc__ == Bar().foo.__doc__ == Foo.foo.__doc__ == "Frobber"
"""

from functools import wraps

class DocInherit(object):
    """
    Docstring inheriting method descriptor

    The class itself is also used as a decorator
    """

    def __init__(self, mthd):
        self.mthd = mthd
        self.name = mthd.__name__

    def __get__(self, obj, cls):
        if obj:
            return self.get_with_inst(obj, cls)
        else:
            return self.get_no_inst(cls)

    def get_with_inst(self, obj, cls):

        overridden = getattr(super(cls, obj), self.name, None)

        @wraps(self.mthd, assigned=('__name__','__module__'))
        def f(*args, **kwargs):
            return self.mthd(obj, *args, **kwargs)

        return self.use_parent_doc(f, overridden)

    def get_no_inst(self, cls):

        for parent in cls.__mro__[1:]:
            overridden = getattr(parent, self.name, None)
            if overridden: break

        @wraps(self.mthd, assigned=('__name__','__module__'))
        def f(*args, **kwargs):
            return self.mthd(*args, **kwargs)

        return self.use_parent_doc(f, overridden)

    def use_parent_doc(self, func, source):
        if source is None:
            raise NameError, ("Can't find '%s' in parents"%self.name)
        func.__doc__ = source.__doc__
        return func

doc_inherit = DocInherit

0 votes

C'est bien qu'une méthode hérite de la docstring de la méthode de la classe parente. Cela serait utile dans de nombreux cas, je pense. Je pensais plus à la docstring de la classe entière, où j'aimerais hériter et ajouter.

0 votes

Ah, je t'ai eu. Dans ce cas, la plupart des générateurs de documents le font déjà pour vous.

48voto

nosklo Points 75862

Vous pouvez concaténer les docstrings facilement :

class Foo(object):
    """
    Foo Class.
    This class foos around.
    """
    pass

class Bar(Foo):
    """
    Bar class, children of Foo
    Use this when you want to Bar around.
    parent:
    """ 
    __doc__ += Foo.__doc__
    pass

Cependant, cela ne sert à rien. La plupart des outils de génération de documentation ( Sphinx y Epydoc inclus) tirera déjà la docstring parent, y compris pour les méthodes. Vous n'avez donc rien à faire.

21 votes

En effet, la plupart des outils de documentation le font. Mais la fonction intégrée help() ne le fait pas.

3 votes

@MarioVilas : c'est peut-être un bug qu'il faut signaler ?

1 votes

Sphinx ne semble pas le faire pour moi, peut-être parce que mon parent est "privé", c'est-à-dire que son nom commence par un trait de soulignement.

7voto

Alex Martelli Points 330805

Pas particulièrement élégant, mais simple et direct :

class X(object):
  """This class has a method foo()."""
  def foo(): pass

class Y(X):
  __doc__ = X.__doc__ + ' Also bar().'
  def bar(): pass

Maintenant :

>>> print Y.__doc__
This class has a method foo(). Also bar().

0 votes

Si vous voulez faire cela pour le Init docstring également, existe-t-il un moyen de le faire dans la définition de la Y ? La seule manière dont j'ai pu le faire est d'utiliser __init__.__doc__ = X.__init__.__doc__ + " Also another param" à la suite de la __init__ définition en Y mais cela semble perturber la mise en forme, en ajoutant des espaces supplémentaires.

7voto

naufraghi Points 533

Un stile mixte qui peut préserver à la fois la syntaxe de la docstring héritée et l'ordre préféré peut être :

class X(object):
  """This class has a method foo()."""
  def foo(): pass

class Y(X):
  """ Also bar()."""
  __doc__ = X.__doc__ + __doc__
  def bar(): pass

Avec la même sortie que celle d'Alex :

>>> print Y.__doc__
This class has a method foo(). Also bar().

De la glace fine : jouer avec la docstring peut rendre votre module inutilisable avec python -OO attendez-vous à en voir :

TypeError: cannot concatenate 'str' and 'NoneType' objects

7voto

Ryan Soklaski Points 215

J'ai écrit Héritage personnalisé pour fournir des outils simples et légers pour gérer l'héritage des chaînes de documents.

Il est également livré avec quelques styles par défaut pour fusionner différents types de docstrings (par exemple, les docstrings formatés Numpy, Google et reST). Vous pouvez également fournir votre propre style très facilement.

Les sections de la docstring qui se chevauchent sont reportées à la section de l'enfant, sinon elles sont fusionnées avec un formatage agréable.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X