pythoninheritancepython-typingabstract-methods

Different argument type in overwritten abstract method


I have different implementations of a feature that only differ in some methods, so I want to use a Parent class to prevent duplicated code. The methods that differ I want to implement with an abstract method to ensure the structure is the same in all children.

The problem is that each child uses its own enums that need to be passed as arguments to the abstract methods. The enums have defined necessary values and can't be combined as the values could overlap. Child1 will only use the Child1 enums, Child2 only Child2 enums (more children are also possible). In addition I'm limited to Python 3.9.

I thought of using typing.Union to allow continuous typing and typing.cast to get the correct type within the methods.

import typing
from abc import ABC, abstractmethod
from enum import IntEnum, IntFlag

class Child1Enum(IntEnum):
    A = 1
    B = 2

class Child1Result(IntFlag):
    W = 1
    X = 4

class Child2Enum(IntEnum):
    C = 1
    D = 2

class Child2Result(IntFlag):
    Y = 1
    Z = 2

class Parent(ABC):
    @abstractmethod
    def abstract_method(
        self, argument: typing.Union[Child1Enum, Child2Enum]
    ) -> typing.Union[Child1Result, Child2Result]:
        pass


class Child1(Parent):
    def abstract_method(
        self, argument: typing.Union[Child1Enum, Child2Enum]
    ) -> typing.Union[Child1Result, Child2Result]:
        argument = typing.cast(Child1Enum, argument)
        # work ...
        return Child1Result.X


class Child2(Parent):
    def abstract_method(
        self, argument: typing.Union[Child1Enum, Child2Enum]
    ) -> typing.Union[Child1Result, Child2Result]:
        argument = typing.cast(Child2Enum, argument)
        # work ...
        return Child2Result.Y

Is this the right way to do it or is there a better way, because I think this works against the idea of abstract methods?


Solution

  • You could make Parent generic:

    import typing
    from abc import ABC, abstractmethod
    from enum import IntEnum, IntFlag
    
    class Child1Enum(IntEnum):
        A = 1
        B = 2
    
    class Child1Result(IntFlag):
        W = 1
        X = 4
    
    class Child2Enum(IntEnum):
        C = 1
        D = 2
    
    class Child2Result(IntFlag):
        Y = 1
        Z = 2
    
    TEnum = typing.TypeVar('TEnum', Child1Enum, Child2Enum)
    TResult = typing.TypeVar('TResult', Child1Result, Child2Result)
    
    class Parent(typing.Generic[TEnum, TResult], ABC):
        @abstractmethod
        def abstract_method(
            self, argument: TEnum
        ) -> TResult:
            pass
    
    
    class Child1(Parent[Child1Enum, Child1Result]):
        def abstract_method(
            self, argument: Child1Enum
        ) -> Child1Result:
            return Child1Result.X
    
    
    class Child2(Parent[Child2Enum, Child2Result]):
        def abstract_method(
            self, argument: Child2Enum
        ) -> Child2Result:
            return Child2Result.Y
    

    Mypy said: Success: no issues found in 1 source file, so I think it should work for you.