Warm tip: This article is reproduced from stackoverflow.com, please click
inheritance python python-sphinx restructuredtext

Sphinx

发布于 2020-03-30 21:11:49

I have some classes that inherit from each other. All classes contain the same method (let us call it mymethod), whereby the children overwrite the base class method. I want to generate a documentation for mymethod in all classes using sphinx.

Suppose mymethod takes an argument myargument. This argument has the same type and meaning for both the base method as well as the inherited method. To minimize redundancies, I would like to write the documentation for myargument only for the base class and insert the documentation in the child method's documentation. That is, I do not want to only put a simple reference to the base class but rather dynamically insert the text when I generate the documentation.

Can this be done? How?

Below please find some code illustrating the problem.

class BaseClass
    def mymethod(myargument):
        """This does something

        Params
        ------
        myargument : int
            Description of the argument

        """
        [...]


class MyClass1(BaseClass):
    def mymethod(myargument):
        """This does something

        Params
        ------
        [here I would like to insert in the description of ``myargument`` from ``BaseClass.mymethod``]
        """

        BaseClass.mymethod(myargument)
        [...]

class MyClass2(BaseClass):
    def mymethod(myargument, argument2):
        """This does something

        Params
        ------
        [here I would like to insert in the description of ``myargument`` in ``BaseClass.mymethod``]
        argument2 : int
            Description of the additional argument

        """

        BaseClass.mymethod(argument)
        [...]


Questioner
Samufi
Viewed
27
Jordan Brière 2020-01-31 17:19

Probably not ideal, but maybe you could use a decorator to extend the docstring. For example:

class extend_docstring:
    def __init__(self, method):
        self.doc = method.__doc__

    def __call__(self, function):
        if self.doc is not None:
            doc = function.__doc__
            function.__doc__ = self.doc
            if doc is not None:
                function.__doc__ += doc
        return function


class BaseClass:
    def mymethod(myargument):
        """This does something

        Params
        ------
        myargument : int
            Description of the argument
        """
        [...]


class MyClass1(BaseClass):
    @extend_docstring(BaseClass.mymethod)
    def mymethod(myargument):
        BaseClass.mymethod(myargument)
        [...]

class MyClass2(BaseClass):
    @extend_docstring(MyClass1.mymethod)
    def mymethod(myargument, argument2):
        """argument2 : int
            Description of the additional argument
        """

        BaseClass.mymethod(argument)
        [...]


print('---BaseClass.mymethod---')
print(BaseClass.mymethod.__doc__)
print('---MyClass1.mymethod---')
print(MyClass1.mymethod.__doc__)
print('---MyClass2.mymethod---')
print(MyClass2.mymethod.__doc__)

Result:

---BaseClass.mymethod---
This does something

        Params
        ------
        myargument : int
            Description of the argument

---MyClass1.mymethod---
This does something

        Params
        ------
        myargument : int
            Description of the argument

---MyClass2.mymethod---
This does something

        Params
        ------
        myargument : int
            Description of the argument
        argument2 : int
            Description of the additional argument

The override method could be resolved dynamically if you make the decorator a descriptor and search for it into __get__ but that means the decorator is no longer stackable as it doesn't return the real function.