Hériter des docstrings dans l'héritage de classe Python

97

J'essaie de faire un héritage de classe en Python. J'aimerais que chaque classe et classe héritée aient de bonnes docstrings. Donc je pense que pour la classe héritée, j'aimerais qu'elle:

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

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

Craig McQueen
la source
2
Je ne peux pas répondre car la question était malheureusement fermée, mais à partir de Python 3.5, inspect.getdocje rechercherai l'arbre d'héritage jusqu'à ce qu'il trouve une docstring.
gerrit
1
Voyez cette réponse .
gerrit

Réponses:

39

Tu n'es pas le seul! Il y a eu une discussion à comp.lang.pythonce sujet il y a quelque temps, et une recette a été créée. Découvrez-le ici .

"""
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 
John Feminella
la source
C'est bien pour une méthode d'hériter de la docstring de la méthode de la classe parente. Ce serait utile dans de nombreux cas, je pense. Je pensais davantage au docstring pour toute la classe, où j'aimerais hériter et ajouter.
Craig McQueen
Ah, gotcha. Dans ce cas, la plupart des doc-génération le font déjà pour vous.
John Feminella
36

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 et Epydoc inclus) extrait déjà la docstring parent, y compris pour les méthodes. Vous n'avez donc rien à faire.

nosklo
la source
16
En effet, la plupart des outils de documentation font cela. Mais la fonction help () intégrée ne le fait pas.
MarioVilas
2
@MarioVilas: c'est peut-être un bug qui devrait être signalé?
naught101
Sphinx ne semble pas faire cela pour moi, peut-être parce que mon parent est "privé", le nom commençant par un trait de soulignement.
Gringo Suave
6

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().
Alex Martelli
la source
Si vous voulez faire cela pour le Init docstringaussi, y a-t-il un moyen de le faire dans la définition de Y? La seule façon dont j'ai pu le faire est d'utiliser __init__.__doc__ = X.__init__.__doc__ + " Also another param"la __init__définition suivante, Ymais cela semble gâcher le formatage, provoquant des espaces supplémentaires supplémentaires.
mgilbert
5

Un style mixte qui peut conserver à la fois la syntaxe héritée de docstring 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 le même résultat que celui d'Alex:

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

Thin Ice: jouer avec docstring peut rendre votre module inutilisable avec python -OO, attendez-vous à:

TypeError: cannot concatenate 'str' and 'NoneType' objects
naufraghi
la source
4

J'ai écrit custom_inherit pour fournir des outils simples et légers pour gérer l'héritage docstring.

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

Les sections de docstring qui se chevauchent seront reportées à la section de l'enfant, sinon elles sont fusionnées avec une mise en forme agréable.

Ryan Soklaski
la source