继承 Python 类继承中的 docstring

我正在尝试用 Python 做一些类继承。我希望每个类和继承的类都有好的文档字符串。所以我认为对于继承类,我希望它是:

  • 继承基类 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?

43804 次浏览

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 译自: 美国《科学》杂志网站(http://code.activestate.com/).

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

然而,这是没有用的。大多数文档生成工具(包括 狮身人面像Epydoc)已经提取了父文档字符串,包括方法。所以你什么都不用做。

不是特别优雅,而是简单直接:

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


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

现在:

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

可以保留继承的 docstring 语法和首选顺序的混合样板可以是:

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


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

输出和 Alex 的一样:

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

薄冰: 玩 docstring 会使你的模块在 python -OO中无法使用,期待一些:

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

我编写 自定义继承是为了提供一些简单、轻量级的工具来处理 docstring 继承。

它还提供了一些合并不同类型文档字符串的默认样式(例如 Numpy、 Google 和 reST 格式的文档字符串)。你也可以很容易地提供你自己的风格。

重叠的 docstring 部分将遵从子部分,否则它们将合并在一起,并具有良好的格式。

这是一个非常古老的线索。但是如果有人正在寻找一种简单的方法,那么您可以使用 __init_subclass__来完成这项工作,无论什么时候继承这个类,__init_subclass__都会被调用。

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]

从 Python 3.5开始,如果子类没有任何文档,docstring 将被继承。

You can use Getdoc () accordingly:

import inspect


class A:
"""Hello"""


class B(A):
pass


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

请注意,内置的 help 函数只对重写的函数递归地拉取,而对 docstring 类则不是,这是由于一些奇怪的原因:

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


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


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