Since you cannot assign a new __doc__
docstring to a class (in CPython at least), you'll have to use a metaclass:
import inspect
def inheritdocstring(name, bases, attrs):
if not '__doc__' in attrs:
# create a temporary 'parent' to (greatly) simplify the MRO search
temp = type('temporaryclass', bases, {})
for cls in inspect.getmro(temp):
if cls.__doc__ is not None:
attrs['__doc__'] = cls.__doc__
break
return type(name, bases, attrs)
Yes, we jump through an extra hoop or two, but the above metaclass will find the correct __doc__
however convoluted you make your inheritance graph.
Usage:
>>> class ParentWithDocstring(object):
... """Parent docstring"""
...
>>> class SubClassWithoutDocstring(ParentWithDocstring):
... __metaclass__ = inheritdocstring
...
>>> SubClassWithoutDocstring.__doc__
'Parent docstring'
The alternative is to set __doc__
in __init__
, as an instance variable:
def __init__(self):
try:
self.__doc__ = next(cls.__doc__ for cls in inspect.getmro(type(self)) if cls.__doc__ is not None)
except StopIteration:
pass
Then at least your instances have a docstring:
>>> class SubClassWithoutDocstring(ParentWithDocstring):
... def __init__(self):
... try:
... self.__doc__ = next(cls.__doc__ for cls in inspect.getmro(type(self)) if cls.__doc__ is not None)
... except StopIteration:
... pass
...
>>> SubClassWithoutDocstring().__doc__
'Parent docstring'
As of Python 3.3 (which fixed issue 12773), you can finally just set the __doc__
attribute of custom classes, so then you can use a class decorator instead:
import inspect
def inheritdocstring(cls):
for base in inspect.getmro(cls):
if base.__doc__ is not None:
cls.__doc__ = base.__doc__
break
return cls
which then can be applied thus:
>>> @inheritdocstring
... class SubClassWithoutDocstring(ParentWithDocstring):
... pass
...
>>> SubClassWithoutDocstring.__doc__
'Parent docstring'