Eredita le docstring nell'ereditarietà della classe Python


97

Sto cercando di eseguire l'ereditarietà delle classi in Python. Vorrei che ogni classe e classe ereditata avesse una buona docstring. Quindi penso che per la classe ereditata, mi piacerebbe che:

  • eredita la docstring della classe base
  • forse aggiungere la documentazione aggiuntiva pertinente alla docstring

Esiste un modo (possibilmente elegante o pitonico) di eseguire questo tipo di manipolazione di docstring in una situazione di ereditarietà di classe? Che ne dici di eredità multipla?


2
Non posso rispondere perché la domanda è stata purtroppo chiusa, ma a partire da Python 3.5, inspect.getdoccercherà nell'albero dell'ereditarietà fino a trovare una docstring.
gerrit

Risposte:


39

Non sei il solo! C'è stata una discussione su comp.lang.pythonquesto argomento qualche tempo fa ed è stata creata una ricetta. Dai un'occhiata qui .

"""
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 

È normale che un metodo erediti la docstring del metodo della classe genitore. Sarebbe utile in molti casi, credo. Stavo pensando di più alla docstring per l'intera classe, dove mi piacerebbe ereditare e aggiungere.
Craig McQueen

Ah, capito. In tal caso, la maggior parte della doc-generation lo fa già per te.
John Feminella

36

Puoi concatenare facilmente le docstring:

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

Tuttavia, questo è inutile. La maggior parte degli strumenti di generazione della documentazione ( Sphinx ed Epydoc inclusi) estrarrà già la docstring principale, inclusi i metodi. Quindi non devi fare nulla.


16
In effetti, la maggior parte degli strumenti di documentazione lo fa. Ma la funzione built-in help () no.
MarioVilas

2
@MarioVilas: forse è un bug che dovrebbe essere segnalato?
nought101

Sphinx sembra non farlo per me, forse perché il mio genitore è "privato", ovvero il nome inizia con un trattino basso.
Gringo Suave

6

Non particolarmente elegante, ma semplice e diretto:

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

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

Adesso:

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

Se vuoi farlo anche per lo Init docstring, c'è un modo per farlo nella definizione di Y? L'unico modo in cui sono stato in grado di farlo è usare __init__.__doc__ = X.__init__.__doc__ + " Also another param"seguendo la __init__definizione in, Yma questo sembra rovinare la formattazione, causando spazi aggiuntivi aggiunti.
mgilbert

5

Uno stile misto che può preservare sia la sintassi della docstring ereditata che l'ordinamento preferito può essere:

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

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

Con lo stesso output di quello di Alex:

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

Ghiaccio sottile: giocare con docstring può rendere inutilizzabile il tuo modulo python -OO, aspettati alcuni:

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

4

Ho scritto custom_inherit per fornire alcuni strumenti semplici e leggeri per la gestione dell'ereditarietà delle docstring.

Inoltre viene fornito con alcuni bei stili predefiniti per unire diversi tipi di docstring (ad esempio Numpy, Google e reST formattati docstrings). Puoi anche fornire il tuo stile molto facilmente.

Le sezioni di docstring sovrapposte verranno rimandate alla sezione del bambino, altrimenti vengono unite insieme con una bella formattazione.

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.