8

I am trying to create a simple abstract base class Abstract that along with its own methods provides the methods of two others abstract base classes: Publisher and Subscriber. When I try to initialize the concrete class Concrete, built on Abstract I get this error: Cannot create a consistent method resolution order (MRO) for bases ABC, Publisher, Subscriber. What is the right way to do it?

from abc import ABC, abstractmethod

class Publisher(ABC):
    
    subscribers = set() 
    
    def register(self, obj):
        self.subscribers.add(obj)
    
    def unregister(self, obj):
        self.subscribers.remove(obj)
    
    def dispatch(self, event):
        print("dispatching", event)
        

class Subscriber(ABC):
    
    @abstractmethod
    def handle_event(self, event):
        raise NotImplementedError
        

class Abstract(ABC, Publisher, Subscriber):
    
    @abstractmethod
    def do_something(self, event):
        raise NotImplementedError
        

class Concrete(Abstract):

    def handle_event(self, event):
        print("handle_event")
    
    def do_something(self, event):
        print("do_something")
    
    

c = Concrete()

3 Answers 3

7

Abstract classes don't have to have abc.ABC in their list of bases. They have to have abc.ABCMeta (or a descendant) as their metaclass, and they have to have at least one abstract method (or something else that counts, like an abstract property), or they'll be considered concrete. (Publisher has no abstract methods, so it's actually concrete.)

Inheriting from ABC is just a way to get ABCMeta as your class's metaclass, for people more comfortable with inheritance than metaclasses, but it's not the only way. You can also inherit from another class with ABCMeta as its metaclass, or specify metaclass=ABCMeta explicitly.


In your case, inheriting from Publisher and Subscriber will already set Abstract's metaclass to ABCMeta, so inheriting from ABC is redundant. Remove ABC from Abstract's base class list, and everything should work.

Alternatively, if you really want ABC in there for some reason, you can move it to the end of the base class list, which will resolve the MRO conflict - putting it first says you want ABC methods to override methods from the other classes, which conflicts with the fact that the other classes are subclasses of ABC.

Sign up to request clarification or add additional context in comments.

2 Comments

PyCharm complain if I have A(metaclass=ABCMeta) and B(A), where B is abstract (doesn't implement all abstract methods of A). Is that just pycharm being silly, or should I explicitly mark B with metaclass=ABCMeta as well?
@olejorgenb: I would consider that PyCharm being silly. Others might consider it a useful warning. Python itself doesn't require you to put metaclass=ABCMeta on B.
3

Change from this:

class Abstract(ABC, Publisher, Subscriber):

To this:

class Abstract(Publisher, Subscriber):

The two subclasses are already abstract, thus you don't need to inherit from ABC again.

Comments

0

class Abstract(Publisher, Subscriber, ABC):

this would be the solution if you want to make sure the class is not instantiable

1 Comment

Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.