Search code examples
pythoninheritanceabstract-class

Python: hierarchy of abstract classes without abstract methods


So, here is a problem:

  1. I want to define an abstract class, let's say AbstractA, which does not require subclasses to implement any of its methods, but rather to extend its functionality. In terms of Java that would be interface class.

  2. Moreover, I want to be able to create an abstract subclass, let's say AbstractB, of the AbstractA with the same properties, but some methods redefining or extending base class methods.

I don't want though to make class (AbstractA) abstract e.g. through the check of class name in __init__ or __new__, because that would require abstract subclass (AbstractB) to redefine that method along with it's main functionality, i.e. construction or initialization of a new instance. Or to call super().__init__(...) which I'd prefer to avoid as well (maybe I'm wrong here).

So, I want something like that:

class AbstractA:
    def __init__(self):
        # do initialization stuff

    def very_common_method(self, ...):
        # do very common stuff

class AbstractB(AbstractA):
    # do not duplicate initialization stuff here, inherit instead

    def less_common_method(self, ...):
        # do less common stuff

class AX(AbstractA):
    def specific_method_1(self, ...):

class BX(AbstractB):
    def specific_method_2(self, ...):

# Instantiating AbstractA or AbstractB should result in error.
# Instantiating AX or BX should not.

Below I have a possible solution. Is there any disadvantages I overlook? Better solution?

Thanks!


Solution

  • Here's possible solution:

    class AbstractA:
        _is_abstract = True
    
        def __init__(self):
            if self._is_abstract:
                raise RuntimeError("Abstract class instantiation.")
            # do initialization stuff
    
        def __init_subclass__(self):   # is called every time class is subclassed
            self._is_abstract = False  # thus makes sure abstract check fails on a subclass
    
    class AbstractMixin:
        def __init_subclass__(self):
            self._is_abstract = True
    
    class AbstractB(AbstractMixin, AbstractA):  # AbstractMixin takes precendence on MRO,
        # inherit __init__                      # so the class abstract check returns True.
    
        def __init_subclass__(self):
            self._is_abstract = False
    
    class A(AbstractA):
        pass
    
    class B(AbstractB):
        pass
    
    AbstractA()
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "<stdin>", line 5, in __init__
    RuntimeError: Abstract class instantiation.
    
    AbstractB()
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "<stdin>", line 5, in __init__
    RuntimeError: Abstract class instantiation.
    
    A()
    <__main__.A object at 0x7f0bba5112e8>
    
    B()
    <__main__.B object at 0x7f0bba511438>