2010-01-08 14 views
70

Estoy tratando de hacer algo de herencia de clase en Python. Me gustaría que cada clase y clase heredada tengan buenas cadenas de documentos. Así que creo que para la clase heredada, me gustaría que:Heredar docstrings en la herencia de clase Python

  • hereda la cadena de documentación de la clase base
  • quizá anexar documentación adicional relevante para la cadena de documentación

¿Hay alguna (posiblemente elegante o pythonic) forma de hacer este tipo de manipulación docstring en una situación de herencia de clase? ¿Qué tal para la herencia múltiple?

Respuesta

32

¡No eres el único! Hubo una discusión sobre comp.lang.python sobre esto hace un tiempo, y se creó una receta. Verifique here.

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

Eso está bien para un método para heredar el docstring del método de la clase padre. Eso sería útil en muchos casos, creo. Estaba pensando más en el docstring para toda la clase, donde me gustaría heredar y anexar. –

+0

Ah, tengo. En ese caso, la mayoría de la generación de doc ya lo hace por usted. –

23

puede concatenar las cadenas de documentación fácilmente:

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 

Sin embargo, esto es inútil. La mayoría de la herramienta de generación de documentación (Sphinx y Epydoc incluida) ya extraerá la carpeta docs principal, incluidos los métodos. Entonces no tienes que hacer nada.

+9

De hecho, la mayoría de las herramientas de documentación lo hacen. Pero la función de ayuda incorporada() no funciona. – MarioVilas

+0

@MarioVilas: tal vez ese es un error que debe ser reportado – naught101

3

No particularmente elegante, pero simple y directo:

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

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

Ahora:

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

Si desea hacer esto para el 'Init docstring' también, ¿hay alguna manera de hacerlo en la definición de' Y'? La única forma en que pude hacerlo es usando '__init__.__ doc__ = X .__ init__.__ doc__ +" También otro parametro "' siguiendo la definición de '__init__' en' Y' pero esto parece interferir con el formateo, causando extra espacios agregados – mgilbert

2

Un montante mixto que puede preservar tanto la sintaxis docstring heredada y el orden preferido puede 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 

Con la misma salida que la de Alex:

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

Hielo fino: jugando con cadena de documentación puede hacer que su módulo inutilizable con python -OO, contar con una cierta:

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

escribí custom_inherit para proporcionar algunas herramientas peso sencillos y ligeros para el manejo de la herencia cadena de documentación.

También viene con algunos buenos estilos predeterminados para fusionar diferentes tipos de documentos (por ejemplo, Numpy, Google y docstrings formateados reST). También puede proporcionar su propio estilo muy fácilmente.

Las secciones de docstring superpuestas pasarán a la sección del elemento secundario, de lo contrario se fusionarán con un buen formato.

Cuestiones relacionadas