开发者

Inherit docstrings in Python class inheritance

开发者 https://www.devze.com 2022-12-16 06:06 出处:网络
I\'m trying to do some class inherit开发者_开发知识库ance in Python. I\'d like each class and inherited class to have good docstrings. So I think for the inherited class, I\'d like it to:

I'm trying to do some class inherit开发者_开发知识库ance in Python. I'd like each class and inherited class to have good docstrings. So I think for the inherited class, I'd like it to:

  • inherit the base class docstring
  • maybe append relevant extra documentation to the docstring

Is there any (possibly elegant or pythonic) way of doing this sort of docstring manipulation in a class inheritance situation? How about for multiple inheritance?


You're not the only one! There was a discussion on comp.lang.python about this a while ago, and a recipe was created. Check it out 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 


You can concatenate the docstrings easily:

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

However, that is useless. Most documentation generation tool (Sphinx and Epydoc included) will already pull parent docstring, including for methods. So you don't have to do anything.


Not particularly elegant, but simple and direct:

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

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

Now:

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


A mixed stile that can preserve both the inherited docstring syntax and the preferred ordering can be:

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

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

With the same output as Alex's one:

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

Thin ice: playing with docstring can make your module unusable with python -OO, expect some:

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


I wrote custom_inherit to provide some simple, light weight tools for handling docstring inheritance.

It also comes with some nice default styles for merging different types of docstrings (e.g. Numpy, Google, and reST formatted docstrings). You can also provide your own style very easily.

Overlapping docstring sections will defer to the child's section, otherwise they are merged together with nice formatting.


Starting with Python 3.5, docstrings are now inherited if a subclass doesn't have any documentation.

You can use inspect.getdoc() accordingly:

import inspect

class A:
  """Hello"""

class B(A):
  pass

class C(B):
  __doc__ = inspect.getdoc(B) + " World"

Do note that the builtin help function pulls recursively only for overridden functions but not for class docstring for some odd reason:

class A:
  """Hello"""
  def test():
    """Testdoc"""

class B(A):
  def test():
    pass

>>> help(B.test)
... Testdoc ...
>>> help(B)
...  # No "Hello".


It is a very old thread. But If anyone is looking for a simple way, you can do this with __init_subclass__ which is called whenever you inherit that class.

def __init_subclass__(cls, **kwargs):
    super().__init_subclass__(**kwargs)
    parent_method_docstr = {}
    for i, v in ParentClass.__dict__.items():
        if v and callable(v) and v.__doc__ is not None:
            parent_method_docstr[i] = v.__doc__

    for i, v in cls.__dict__.items():
        if v and callable(v) and v.__doc__ is None and i in parent_method_docstr:
            v.__doc__ = parent_method_docstr[i]
0

精彩评论

暂无评论...
验证码 换一张
取 消