在Python类继承中继承文档字符串

127 投票
7 回答
54204 浏览
提问于 2025-04-15 17:47

我正在尝试在Python中进行类的继承。我希望每个类和继承的类都有好的文档字符串(docstring)。所以我想对于继承的类,我希望它能够:

  • 继承基类的文档字符串
  • 可能在文档字符串后面添加一些相关的额外说明

有没有什么方法可以优雅地处理这种文档字符串的操作,特别是在类继承的情况下?如果是多重继承呢?

7 个回答

9

从Python 3.5开始,如果一个子类没有自己的文档说明,它会自动继承父类的文档说明。

你可以使用 inspect.getdoc() 来查看这些文档说明:

import inspect

class A:
  """Hello"""

class B(A):
  pass

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

需要注意的是,内置的help函数在处理被重写的函数时会递归查找文档说明,但对于类的文档说明,它却出于某种奇怪的原因不这样做:

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

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

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

你可以很简单地把文档字符串连接起来:

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

不过,这样做其实没什么用。大多数文档生成工具(比如SphinxEpydoc)会自动提取父类的文档字符串,包括方法的文档。所以你根本不需要做任何额外的工作。

55

你不是唯一一个有这个疑问的人!之前在 comp.lang.python 这个讨论区里也有人聊过这个话题,还分享了一个解决方案。你可以在这里看看。

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

撰写回答