Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Why is the initialiser of the inherited class skipped?

While setting up a class hierarchy that involves multiple inheritance I came across problems with the order of constructor/initialiser calls. To help me analyse I set up a minimal, contrived example to reproduce the issue:

There is a pair of base classes to represent mutable and immutable objects.
BaseView allows only viewing the value member, while Base is-a BaseView plus it allows to set the value member.

And then there is a specialised set of classes that inherit from BaseView and Base respectively and adds counting of the accesses per type.

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

class BaseView:
    def __init__(self):
        print("BaseView()")
        self._data = None
        self._locked = False

    def get(self):
        if self._data:
            return self._data
        else:
            raise RuntimeError

class Base(BaseView):
    def __init__(self):
        print("Base()")
        super().__init__()

    def set(self, _d: int):
        if not self._locked:
            self._data = _d
            self._locked = True
        else:
            raise RuntimeError

class TypeAView(BaseView):
    def __int__(self):
        print('TypeAView()')
        super().__init__()
        self._numViews = 0

    def get(self):
        self._numViews += 1
        return super().get()

class TypeA(TypeAView, Base):
    def __int__(self):
        print('TypeA()')
        super().__init__()
        self._numWrites = 0

    def set(self, _d: int):
        self._numWrites += 1
        self._locked = False
        super().set(_d)
        self._locked = True


if __name__ == '__main__':
    a = TypeA()
    assert hasattr(a, '_numWrites')
    val = 42
    a.set(val)
    ret = a.get()
    assert val == ret

To allow to follow the initialisation order I’ve added prints to the __init__ methods.

When I construct an object of TypeA I’d expect an initialiser call chain that respects the MRO:

TypeA.mro()
[__main__.TypeA, __main__.TypeAView, __main__.Base, __main__.BaseView, object]

What I get instead is

Base()
BaseView()

This will give me an incomplete object of TypeA
To prove that I’ve added an assertion with a membership check, that indeed fires.

Traceback (most recent call last):
  File "[...]/minimal_example.py", line 52, in <module>
    assert hasattr(a, '_numWrites')
AssertionError

Maybe my C++ reflexes are still too strong, but I simply cannot get my head around how Python creates this object and its different parts.
Why does it simply skip the concrete classes’ initialiser and directly jumps to the base? Any pointers are appreciated.

>Solution :

The issue in your code is a typo error. You have __int__ instead of __init__ in the TypeAView and TypeA classes. Because of this , the constructors are not called instead, the base class constructors are being called.

Try this

class BaseView:
    def __init__(self):
        print("BaseView()")
        self._data = None
        self._locked = False

    def get(self):
        if self._data:
            return self._data
        else:
            raise RuntimeError

class Base(BaseView):
    def __init__(self):
        print("Base()")
        super().__init__()

    def set(self, _d: int):
        if not self._locked:
            self._data = _d
            self._locked = True
        else:
            raise RuntimeError

class TypeAView(BaseView):
    def __init__(self): # removed typo
        print('TypeAView()')
        super().__init__()
        self._numViews = 0

    def get(self):
        self._numViews += 1
        return super().get()

class TypeA(TypeAView, Base):
    def __init__(self): # removed typo
        print('TypeA()')
        super().__init__()
        self._numWrites = 0

    def set(self, _d: int):
        self._numWrites += 1
        self._locked = False
        super().set(_d)
        self._locked = True


if __name__ == '__main__':
    a = TypeA()
    assert hasattr(a, '_numWrites')
    val = 42
    a.set(val)
    ret = a.get()
    assert val == ret
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading