Docstrings overnemen in Python-klasseovererving

Ik probeer een klasse-overerving uit te voeren in Python. Ik zou graag willen dat elke klas en geërfde klas goede docstrings heeft. Dus ik denk dat ik voor de geërfde klasse het volgende zou willen:

  • erft de basisklasse docstring
  • misschien voeg relevante extra documentatie toe aan de docstring

Is er een (mogelijk elegante of pythonische) manier om dit soort docstring-manipulatie uit te voeren in een situatie met klassenovererving? Hoe zit het met meervoudige overerving?


Antwoord 1, autoriteit 100%

Je bent niet de enige! Er was een tijdje geleden een discussie hierover op comp.lang.pythonen er is een recept voor gemaakt. Bekijk het hier.

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

Antwoord 2, autoriteit 98%

Je kunt de docstrings gemakkelijk samenvoegen:

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

Dat is echter nutteloos. De meeste hulpprogramma’s voor het genereren van documentatie (Sphinxen Epydocinbegrepen) zal al bovenliggende docstring ophalen, ook voor methoden. U hoeft dus niets te doen.


Antwoord 3, autoriteit 16%

Niet bijzonder elegant, maar eenvoudig en direct:

class X(object):
  """This class has a method foo()."""
  def foo(): pass
class Y(X):
  __doc__ = X.__doc__ + ' Also bar().'
  def bar(): pass

Nu:

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

Antwoord 4, autoriteit 16%

Een gemengde stijl die zowel de overgeërfde docstring-syntaxis als de voorkeursvolgorde kan behouden, kan zijn:

class X(object):
  """This class has a method foo()."""
  def foo(): pass
class Y(X):
  """ Also bar()."""
  __doc__ = X.__doc__ + __doc__
  def bar(): pass

Met dezelfde output als die van Alex:

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

Dun ijs:spelen met docstring kan je module onbruikbaar maken met python -OO, verwacht wat:

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

Antwoord 5, autoriteit 13%

Ik heb custom_inheritgeschreven om een aantal eenvoudige, lichtgewicht tools te bieden voor het afhandelen van docstring-overerving.

Het wordt ook geleverd met een aantal mooie standaardstijlen voor het samenvoegen van verschillende soorten docstrings (bijv. Numpy, Google en reST-geformatteerde docstrings). Je kunt ook heel gemakkelijk je eigen stijl geven.

Overlappende docstring-secties worden verplaatst naar de sectie van het kind, anders worden ze samengevoegd met een mooie opmaak.

Other episodes