Наследование строк документации в наследовании классов Python

97

Я пытаюсь наследовать классы в Python. Я бы хотел, чтобы у каждого класса и унаследованного класса были хорошие строки документации. Поэтому я думаю, что для унаследованного класса я бы хотел:

  • наследовать строку документации базового класса
  • возможно, добавить соответствующую дополнительную документацию в строку документации

Есть ли какой-либо (возможно, элегантный или питонический) способ выполнения такого рода манипуляций со строкой документации в ситуации наследования классов? Как насчет множественного наследования?

Крейг МакКуин
источник
2
Я не могу ответить, потому что вопрос, к сожалению, закрыт, но начиная с Python 3.5, он inspect.getdocбудет искать в дереве наследования, пока не найдет строку документации.
gerrit
1
Смотрите этот ответ .
gerrit

Ответы:

39

Ты не единственный! Некоторое 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 
Джон Феминелла
источник
Это замечательно для метода, который наследует строку документации метода родительского класса. Думаю, это было бы полезно во многих случаях. Я больше думал о строке документации для всего класса, которую я хотел бы наследовать и добавлять.
Крейг Маккуин,
Ах, попался. В этом случае большая часть создателей документов уже делает это за вас.
Джон Феминелла
36

Вы можете легко объединить строки документации:

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

Однако это бесполезно. Большинство инструментов создания документации ( включая Sphinx и Epydoc ) уже извлекают родительскую строку документации, в том числе для методов. Так что вам не нужно ничего делать.

носкло
источник
16
Действительно, большинство инструментов документации это делают. Но встроенная функция help () этого не делает.
MarioVilas
2
@MarioVilas: возможно, это ошибка, о которой следует сообщить?
naught101 05
Сфинкс, похоже, не делает этого для меня, возможно, потому, что мой родитель является «частным», иначе имя начинается с подчеркивания.
Gringo Suave
6

Не особо элегантно, но просто и прямо:

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().
Алекс Мартелли
источник
Если вы хотите сделать то же самое Init docstringи для, есть ли способ сделать это в определении Y? Единственный способ, которым я смог это сделать, - это __init__.__doc__ = X.__init__.__doc__ + " Also another param"следовать __init__определению, Yно это, похоже, нарушает форматирование, вызывая лишние пробелы.
mgilbert
5

Смешанный стиль, который может сохранить как унаследованный синтаксис строки документации, так и предпочтительный порядок, может быть:

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

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

С тем же результатом, что и у Алекса:

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

Тонкий лед: игра со строкой документации может сделать ваш модуль непригодным для использования python -OO, ожидайте некоторых:

TypeError: cannot concatenate 'str' and 'NoneType' objects
Науфраги
источник
4

Я написал custom_inherit, чтобы предоставить несколько простых и легких инструментов для обработки наследования строк документации.

Он также поставляется с некоторыми хорошими стилями по умолчанию для слияния различных типов строк документации (например, строк документации в формате Numpy, Google и reST). Вы также можете очень легко создать свой собственный стиль.

Перекрывающиеся разделы строки документации будут относиться к дочернему разделу, в противном случае они будут объединены вместе с хорошим форматированием.

Райан Сокласки
источник