Search code examples
pythonclassmethodsattributesdocstring

Python classes: having instance method in one class point to instance method in another class


I have a class (I will call it "master class") that uses instance methods from multiple other classes. The other classes are imported on __init__ and are stored as private instance attributes.

I would like to use the other class's instance methods, with the following properties:

  • Not rewrite docstrings or signature in the master class
  • Have autodoc parse the docstrings from the other classes as if they were docstrings for the master class

Currently, the way I have it set up:

class OtherClass:
    """Some other class that I import."""

    def __init__(self):
        pass

    def print_num(self, num: float = 15) -> None:
        """Print a num.

        Args:
            num: Number to print

        """
        print(num)

from .other import OtherClass


class MasterClass:

    def __init__(self, other_class: OtherClass):
        """Create master class with a bunch of other classes.

        For simplicity, I only included one class here as an arg.

        Args:
            other_class: Houses some methods

        """
        self._other_class = other_class

    def print_num(self, num: float = 15):
        """Print a num.

        Args:
            num: Number to print

        """
        self._other_class.print_num(num)

To pull in OtherClass.print_num, I had to:

  1. Manually copy the signature
  2. Manually copy the docstring (because I want my Sphinx documentation to be accurate)
  3. Call OtherClass's method within MasterClass's method, passing in all the args and kwargs manually

Is there a better way to do this?

Thank you in advance for your guidance.


Solution

  • The function wraps from the module functools is what you need:

    from functools import wraps
    
    class OtherClass:
        def print_num(self, num: float=15) -> None:
            """Print a num.
    
            Args:
                num: Number to print
    
            """
            print(num)
    
    class MasterClass:
        def __init__(self, other_class: OtherClass):
            self._other_class = other_class
    
        @wraps(OtherClass.print_num)
        def print_num(self, num=15):
            self._other_class.print_num(num)
    
    print(MasterClass.print_num.__doc__)
    print(MasterClass.print_num.__annotations__)
    

    Output:

    Print a num.
    
            Args:
                num: Number to print
    
    
    {'num': <class 'float'>, 'return': None}
    

    You still have to make the explicit call.

    Note: your design is a special case of the facade pattern.