Herdar docstrings na herança de classe Python


97

Estou tentando fazer alguma herança de classe em Python. Eu gostaria que cada classe e classe herdada tivessem boas docstrings. Então eu acho que para a classe herdada, eu gostaria que:

  • herdar a docstring da classe base
  • talvez anexe documentação extra relevante ao docstring

Existe alguma maneira (possivelmente elegante ou pythônica) de fazer esse tipo de manipulação de docstring em uma situação de herança de classe? Que tal para herança múltipla?


2
Não posso responder porque a pergunta foi encerrada infelizmente, mas a partir do Python 3.5, inspect.getdocpesquisarei a árvore de herança até encontrar uma docstring.
gerrit

1

Respostas:


39

Você não é o único! Houve uma discussão comp.lang.pythonsobre isso há algum tempo e uma receita foi criada. Confira aqui .

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

Isso é ótimo para um método herdar a docstring do método da classe pai. Isso seria útil em muitos casos, eu acho. Eu estava pensando mais sobre a docstring para toda a classe, onde gostaria de herdar e anexar.
Craig McQueen

Ah, entendi. Nesse caso, a maior parte da geração de documentos já faz isso por você.
John Feminella

36

Você pode concatenar as docstrings facilmente:

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

No entanto, isso é inútil. A maioria das ferramentas de geração de documentação ( Sphinx e Epydoc incluídos) já puxará docstring pai, incluindo métodos. Portanto, você não precisa fazer nada.


16
Na verdade, a maioria das ferramentas de documentação faz isso. Mas a função interna help () não.
MarioVilas

2
@MarioVilas: talvez seja um bug que deva ser relatado?
naught101

Sphinx não parece estar fazendo isso por mim, talvez porque meu pai seja "privado", também conhecido como nome começa com um sublinhado.
Gringo Suave

6

Não é particularmente elegante, mas simples e direto:

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

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

Agora:

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

Se você quiser fazer isso também para o Init docstring, há uma maneira de fazer isso na definição de Y? A única maneira que consegui fazer é __init__.__doc__ = X.__init__.__doc__ + " Also another param"seguindo a __init__definição em, Ymas isso parece bagunçar a formatação, causando espaços extras adicionados.
mgilbert de

5

Um estilo misto que pode preservar a sintaxe de docstring herdada e a ordem preferencial pode ser:

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

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

Com a mesma saída de Alex:

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

Gelo fino: brincar com docstring pode tornar seu módulo inutilizável com python -OO, espere alguns:

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

4

Eu escrevi custom_inherit para fornecer algumas ferramentas simples e leves para lidar com a herança de docstring.

Ele também vem com alguns estilos padrão legais para mesclar diferentes tipos de docstrings (por exemplo, Numpy, Google e docstrings formatados com reST). Você também pode fornecer seu próprio estilo com muita facilidade.

Sobrepor seções docstring irá adiar para a seção filho, caso contrário, elas são mescladas com uma boa formatação.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.