Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Multiple sequential abstract base classes in Python 3.4+

Take a common vehicle inheritance example where Vehicle and Car are ABCs and the latter inherits from the former. We then have e.g. FordMustang which is not abstract and should inherit from Car.

So, we have

class Vehicle(ABC):

    @abstractmethod
    def abstract_vehicle_method(self):
        pass

Which is presumably all fine and dandy. However, for the Car class, I am not sure whether it should inherit only from Vehicle or from Vehicle and ABC, and if so, in what order? Moreover, I would like to force abstract_vehicle_method() to be defined in FordMustang and all such non-abstract classes, so should I repeat the abstract_vehicle_method definition in Car or will the inheritance sort this out?

To enumerate the options:

Should it be

class Car(Vehicle):
    [...]

or

class Car(ABC, Vehicle):
    [...]

or

class Car(Vehicle, ABC):
    [...]

and (assuming the first is correct for simplicity) should it be

class Vehicle(ABC):

    @abstractmethod
    def abstract_vehicle_method(self):
        pass

class Car(Vehicle):

    @abstractmethod
    def abstract_vehicle_method(self):
        pass

    # Some car specific defs here

class FordMustang(Car):

    def abstract_vehicle_method(self):
        # Concrete def here

or just

class Vehicle(ABC):

    @abstractmethod
    def abstract_vehicle_method(self):
        pass

class Car(Vehicle):

    # Some car specific defs here

class FordMustang(Car):

    def abstract_vehicle_method(self):
        # Concrete def here
like image 785
P. Lau Avatar asked Aug 05 '26 12:08

P. Lau


1 Answers

You can directly inherit from Vehicle; the metaclass that makes Vehicle 'abstract' is inherited along with it. You do not need to mix in ABC for those subclasses.

You also don't need to re-define the method. It is inherited too, along with it's 'abstractness'.

Under the hood, the metaclass tracks what attributes are 'abstract'; as long as there are any, the class can't be used to create instances. You can add more abstract methods, or by providing a different attribute for the same name, remove abstract methods from the set.

Demo for your specific example:

>>> from abc import ABC, abstractmethod
>>> class Vehicle(ABC):
...     @abstractmethod
...     def abstract_vehicle_method(self):
...         pass
...
>>> type(Vehicle)  # it's an abstract class
<class 'abc.ABCMeta'>
>>> Vehicle.__abstractmethods__  # this set determines what is still abstract
frozenset({'abstract_vehicle_method'})
>>> class Car(Vehicle):
...     pass
...
>>> type(Car)  # still an abstract class
<class 'abc.ABCMeta'>
>>> Car.__abstractmethods__  # still has abstract methods, incl. inherited methods
frozenset({'abstract_vehicle_method'})
>>> class FordMustang(Car):
...     def abstract_vehicle_method(self):
...         pass
...
>>> type(FordMustang)  # still an abstract class
<class 'abc.ABCMeta'>
>>> FordMustang.__abstractmethods__  # but with no abstract methods left
frozenset()
>>> FordMustang()  # so we can create an instance
<__main__.FordMustang object at 0x106054f98>
like image 79
Martijn Pieters Avatar answered Aug 08 '26 01:08

Martijn Pieters



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!