0

I'd like to define an abstract class which inherits from another abstract class.

class DataBuffer(ABC):
    def __init__(self):
        self.buffered_data = {}

class IMUDataBuffer(ABC, DataBuffer):
    def __init__(self):
        super(IMUDataBuffer, self).__init__()

    def update(self, message):
        timestamp = message['ts']
        x_val, y_val, z_val = message[self.message_key]
        self.buffered_data[timestamp] = (x_val, y_val, z_val)

class GyroscopeDataBuffer(IMUDataBuffer):
    def __init__(self):
        super(GyroscopeDataBuffer, self).__init__()
        self.message_key = 'gy'

class AccelerometerDataBuffer(IMUDataBuffer):
    def __init__(self, message):
        super(AccelerometerDataBuffer, self).__init__()
        self.message_key = 'ac'

In this example, the IMUDataBuffer should inherit methods from DataBuffer, but it itself will not ever be instantiated, and thus should be an abstract class.

This is the error that I receive with the above code:

TypeError: Cannot create a consistent method resolution order (MRO) for bases ABC, DataBuffer

As a cheap work around, I can define a message_key in the IMUDatabuffer, but this feels sloppy to me, as really the IMUDataBuffer shouldn't have a message_key property.

class IMUDataBuffer(DataBuffer):
    def __init__(self):
        super(IMUDataBuffer, self).__init__()
        self.message_key = None

What is the proper way to handle the inheritance here?

Kevin King
  • 23
  • 7

3 Answers3

0

I propose to remove the ABC in IMUDataBuffer parent list as DataBuffer already has ABC as parent.

ABC -> DataBuffer -> IMUDataBuffer -> Gyroscope
                                   -> Accelerometer

The code would look like this:

class ABC:
    pass

class DataBuffer(ABC):  # <-- brings already all stuff from ABC
    def __init__(self):
        self.buffered_data = {}

class IMUDataBuffer(DataBuffer):  # <-- ABC removed here
    def __init__(self):
        super(IMUDataBuffer, self).__init__()

    [...]

Then the error disappears.

Manuel
  • 534
  • 3
  • 9
  • Thanks! This seems to work, but I'm getting an "unresolved attribute reference" from pycharm for the message_key variable. Is it possible that this class still isn't truly being recognized as an ABC? – Kevin King Jul 26 '19 at 17:40
  • You can inherit from multiple classes in Python. But in this case it's not necessary. – JBGreen Jul 26 '19 at 18:15
  • @KevinKing what is the class definition of ABC? It's right that self.message_key does not exist in IMUDataBuffer, Pycharm is totally right. – Manuel Jul 26 '19 at 18:44
  • ABC is the AbstractBaseClass in the default abc library. ```from abc import ABC``` – Kevin King Jul 26 '19 at 21:49
  • ABC looked to me like foo, bar, 123... I'll take a look at the docs. Thanks, @KevinKing ! – Manuel Jul 26 '19 at 21:53
0

Here is fix -

from abc import *

class DataBuffer(ABC):
    def __init__(self):
        self.buffered_data = {}

class IMUDataBuffer(DataBuffer, ABC):
    def __init__(self):
        super(IMUDataBuffer, self).__init__()

    def update(self, message):
        timestamp = message['ts']
        x_val, y_val, z_val = message[self.message_key]
        self.buffered_data[timestamp] = (x_val, y_val, z_val)

Also refer this for detail explaination - Why is this an ambiguous MRO?

codinnvrends
  • 264
  • 2
  • 8
  • This seems close, but when I try to instantiate the GyroscopeDataBuffer class, I get a ```TypeError: __new__() missing 3 required positional arguments: 'name', 'bases', and 'namespace' ``` – Kevin King Jul 26 '19 at 18:01
  • I have updated the code. I was using python 2.7. Corrected it for python 3. See your code working here - https://repl.it/repls/DetailedAngryInstitution – codinnvrends Jul 26 '19 at 18:18
0

In Python, simply inheriting from ABC doesn't prevent instantiation. This works fine:

from abc import ABC

class Foo(ABC):
    pass

foo = Foo()

Instantiation will only raise an error if there are unimplemented abstractmethods.

Your intermediate class doesn't have to inherit from ABC, just from DataBuffer. As long as there are unimplemented abstractmethods instantiation will fail.

from abc import ABC, abstractmethod

class Foo(ABC):
    @abstractmethod
    def do_thing(self):
        pass

class Bar(Foo):
    pass

bar = Bar()  # Instantiation raises TypeError because Bar does not implement do_thing

Since Bar inherits from Foo which is an abstract class, Bar is also an abstract class:

>>> type(Foo)
<class 'abc.ABCMeta'>
>>> type(Bar)
<class 'abc.ABCMeta'>

Compare with some other random class:

>>> class Baz:
>>>    pass
>>> type(Baz)
<class 'type'>
JBGreen
  • 534
  • 2
  • 6