How to write let two Python abstract classes implement each other's abstract methods?

144 Views Asked by At

Suppose you have abstract classes A1 and A2. Each of them has an abstract method and a concrete method.

from abc import ABC, abstractmethod

class A0(ABC):
    pass

class A1(A0, ABC):
    def foo(self):
        return 1

    @abstractmethod
    def bar(self):
        raise NotImplementedError()


class A2(A0, ABC):
    @abstractmethod
    def foo(self):
        raise NotImplementedError()
    
    def bar(self):
        return 10

Now you want to mix-in them togather to implement each other's abstract method:

class C(A2, A1, A0):
    def all(self):
        return (super().foo(), super().bar())


C().all()

However the above code does not work because I got the following error:

TypeError: Can't instantiate abstract class C with abstract method foo

How can I create C so that it can mix-in both A1 and A2?

2

There are 2 best solutions below

0
Yang Bo On BEST ANSWER

This is a solution similar to Gábor Fekete, except that the abstract methods are extracted to new ABC's, to avoid changing A0.

from abc import ABC, abstractmethod

class A0(ABC):
    pass

class AbstractFoo(A0, ABC):
    @abstractmethod
    def foo(self):
        raise NotImplementedError()

class AbstractBar(A0, ABC):
    @abstractmethod
    def bar(self):
        raise NotImplementedError()

class A1(AbstractFoo, AbstractBar, A0, ABC):
    def foo(self):
        return 1

class A2(AbstractFoo, AbstractBar, A0, ABC):
    def bar(self):
        return 10

class C(A2, A1, A0):
    def all(self):
        return (super().foo(), super().bar())

C().all()

1
Gábor Fekete On

I moved the abstract methods to A0, and only implemented one of each in the A1, A2 classes, then C has both of them implemented:

from abc import ABC, abstractmethod

class A0(ABC):
    @abstractmethod
    def foo(self):
        raise NotImplementedError()

    @abstractmethod
    def bar(self):
        raise NotImplementedError()

class A1(A0, ABC):
    def foo(self):
        return 1

class A2(A0, ABC):
    def bar(self):
        return 10

class C(A1,A2):
    def all(self):
        return (super().foo(), super().bar())


print(C().all())
# can't instantiate A0,A1,A2, as these have not implemented abstract methods
# print(A1()) # won't work
# print(A2()) # won't work