Search code examples
pythonpython-3.xdispatchmultimethod

How to dispatch methods of a superclass?


I would like to decorate the shapely.geometry.Point class to instantiate it from a dendropy.datamodel.basemodel.AnnotationSet object. I chose the multimethod package to dispatch __init__ of the superclass as it dispatches this method into a simple class without any problems:

from multimethod import multimeta

class Example(metaclass=multimeta):
    def __init__(self, *args, **kwargs):
        print("DEFAULT")

    def __init__(self, x: str):
        print("Initialising from a string")

    def __init__(self, x: int):
        print("Initialising from an integer")

However, it does not work in my case with the inheritance:

class Point(Point, metaclass=multimeta):
    def __init__(self, annotations: AnnotationSet):
        try:
            super().__init__(
                float(annotations.get_value(LONGITUDE_ALIAS)),
                float(annotations.get_value(LATITUDE_ALIAS)),
            )
        except (TypeError, ValueError):
            raise ValueError(
                f"There is no coordinates in the annotations:\n{annotations}"
            )

It initialises from AnnotationSet but not from default arguments:

Point(6, 4)

---------------------------------------------------------------------------
DispatchError                             Traceback (most recent call last)
Cell In [461], line 1
----> 1 Point(6, 4)

File ~\anaconda3\envs\bioinfo\lib\site-packages\multimethod\__init__.py:313, in multimethod.__call__(self, *args, **kwargs)
    311 if self.pending:  # check first to avoid function call
    312     self.evaluate()
--> 313 func = self[tuple(func(arg) for func, arg in zip(self.type_checkers, args))]
    314 try:
    315     return func(*args, **kwargs)

File ~\anaconda3\envs\bioinfo\lib\site-packages\multimethod\__init__.py:307, in multimethod.__missing__(self, types)
    305     return self.setdefault(types, *funcs)  # type: ignore
    306 msg = f"{self.__name__}: {len(keys)} methods found"  # type: ignore
--> 307 raise DispatchError(msg, types, keys)

DispatchError: ('__init__: 0 methods found', (<class '__main__.Point'>, <class 'int'>), [])

Are there ways to dispatch the superclass methods? I am not interested in multimethod specifically; maybe there is any way besides?


Solution

  • This is a paraphrase of Coady's answer with their consent.

    To dispatch a supermethod, we need to specify it manually, wrapping it into a multimethod instance & using the register method as a decorator for the first of new multimethods. For instance, in my case:

    class Point(Point):
        @multimethod(Point.__init__).register
        def __init__(self, annotations: AnnotationSet):
            try:
                super().__init__(
                    float(annotations.get_value(LONGITUDE_ALIAS)),
                    float(annotations.get_value(LATITUDE_ALIAS)),
                )
            except (TypeError, ValueError):
                raise ValueError(
                    f"There is no coordinates in the annotations:\n{annotations}"
                )