J'essaie de faire un héritage de classe en Python. J'aimerais que chaque classe et classe héritée ait de bonnes docstrings. Je pense donc que pour la classe héritée, j'aimerais:
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? Et pour l'héritage multiple?
Tu n'es pas le seul! Il y a eu une discussion sur comp.lang.python
à ce sujet il y a quelque temps, et une recette a été créée. Vérifiez-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
Vous pouvez concaténer facilement les docstrings:
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
Mais cela est inutile. La plupart des outils de génération de documentation ( Sphinx et Epydoc inclus) tireront déjà la docstring parent, y compris pour les méthodes. Vous n'avez donc rien à faire.
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().
Un stile mixte qui peut conserver à la fois la syntaxe de la chaîne de documents héritée 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 la même sortie que celle 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
J'ai écrit custom_inherit pour fournir des outils simples et légers pour gérer l'héritage de docstring.
Il est également livré avec certains styles par défaut Nice pour fusionner différents types de docstrings (par exemple Numpy, Google et docSTings formatés reST). Vous pouvez également fournir votre propre style très facilement.
Les sections de docstring qui se chevauchent se reportent à la section de l'enfant, sinon elles sont fusionnées avec le formatage Nice.