Dziedziczenie ciągów dokumentów w dziedziczeniu klas w Pythonie


97

Próbuję zrobić dziedziczenie klas w Pythonie. Chciałbym, żeby każda klasa i klasa odziedziczona miała dobre dokumenty. Więc myślę, że dla klasy odziedziczonej chciałbym:

  • dziedziczy dokumentację klasy bazowej
  • może dołączyć odpowiednią dodatkową dokumentację do dokumentu

Czy istnieje jakikolwiek (prawdopodobnie elegancki lub pytoniczny) sposób wykonywania tego rodzaju manipulacji dokumentacją w sytuacji dziedziczenia klas? Co powiesz na wielokrotne dziedziczenie?


2
Nie mogę odpowiedzieć, ponieważ pytanie zostało niestety zamknięte, ale od Pythona 3.5 inspect.getdocbędzie przeszukiwać drzewo dziedziczenia, aż znajdzie ciąg dokumentów.
— gerrit

1
Zobacz tę odpowiedź .
— gerrit

Odpowiedzi:


39

Nie jesteś jedyny! Niedawno odbyła się dyskusja na comp.lang.pythonten temat i powstał przepis. Sprawdź to tutaj .

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

To fajne, żeby metoda dziedziczyła dokumentację metody klasy nadrzędnej. Myślę, że byłoby to przydatne w wielu przypadkach. Bardziej myślałem o dokumencie dla całej klasy, w którym chciałbym dziedziczyć i dodawać.
— Craig McQueen

Ach, rozumiem. W takim przypadku większość pokolenia dokumentów już to robi.
— John Feminella,

36

Możesz łatwo łączyć dokumenty:

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

Jednak jest to bezużyteczne. Większość narzędzi do tworzenia dokumentacji (w tym Sphinx i Epydoc ) będzie już pobierać macierzysty ciąg dokumentów, w tym dla metod. Więc nie musisz nic robić.


16
Rzeczywiście, większość narzędzi do dokumentacji to robi. Ale wbudowana funkcja help () nie.
— MarioVilas

2
@MarioVilas: może to błąd, który należy zgłosić?
— naught101

Wydaje się, że Sfinks nie robi tego za mnie, być może dlatego, że mój rodzic jest „prywatny”, czyli imię zaczyna się od podkreślenia.
— Gringo Suave

6

Niezbyt eleganckie, ale proste i bezpośrednie:

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

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

Teraz:

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

Jeśli chcesz to zrobić Init docstringrównież w przypadku, czy istnieje sposób, aby to zrobić w definicji Y? Jedynym sposobem udało mi się zrobić to przy użyciu __init__.__doc__ = X.__init__.__doc__ + " Also another param"zgodnie z __init__definicją zawartą w Yale to wydaje się bałagan z formatowaniem, powodując dodatkowo dodawane spacje.
— mgilbert

5

Mieszany styl, który może zachować zarówno dziedziczoną składnię ciągów dokumentów, jak i preferowaną kolejność, może być:

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

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

Z takim samym wyjściem jak Alex:

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

Cienki lód: zabawa z ciągiem dokumentów może sprawić, że twój moduł będzie bezużyteczny python -OO, spodziewaj się:

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

4

Napisałem custom_inherit, aby zapewnić proste, lekkie narzędzia do obsługi dziedziczenia dokumentów.

Zawiera również kilka ładnych domyślnych stylów do łączenia różnych typów ciągów dokumentów (np. Numpy, Google i reST). Możesz również bardzo łatwo nadać swój własny styl.

Nakładające się sekcje docstringowe będą odkładały się do sekcji dziecka, w przeciwnym razie zostaną połączone razem z ładnym formatowaniem.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.