Multiple inheritance and a broken super() chain

Chris Angelico rosuav at gmail.com
Tue Jul 4 20:27:37 EDT 2023


On Wed, 5 Jul 2023 at 08:35, Alan Gauld via Python-list
<python-list at python.org> wrote:
>
> On 03/07/2023 19:39, Chris Angelico via Python-list wrote:
> > On Tue, 4 Jul 2023 at 03:39, Peter Slížik via Python-list
> >> The legacy code I'm working with uses a classic diamond inheritance.
>
> > What happens when Top is initialized twice? This seems like a problem
> > waiting to happen, and when you moved to using super(), you more than
> > likely simplified things and fixed things.
>
> Slightly off topic but I wonder how many real world problems
> people have experienced having the top of a diamond initialized
> twice? The reason I ask is that I ran a maintenance team for
> about 5 years (early 1990s) working on various OOP projects using MI;
> in Lisp Flavors, C++(*) and a homebrew variant of C that supported MI.
> In that time I don't recall ever having problems with top objects
> being initialized twice (apart from redundant execution of code
> of course).

It's important to distinguish between diamond inheritance and what the
OP seemed to expect, which was independent hierarchies. (In C++ terms,
that's virtual inheritance and .... the unnamed default type of MI.
Non-virtual inheritance??) With independent hierarchies, the object is
composed of two subobjects, each with its own regular
single-inheritance tree, and unless you need to call a method on the
duplicated grandparent from the second parent (in which case you have
to cast before calling), it's perfectly natural to treat them
separately. But with virtual inheritance - as is always the case in
Python - there is only one object.

Whether it's a problem depends entirely on what the initializer does.
If it's idempotent and doesn't depend on any arguments that it didn't
already get, we're fine! But if it does something like this:

class Example:
    def __init__(self):
        self.button = Some_GUI_Library.Button("Example")
        self.button.add_to_window()

then calling init twice will create a second button.

It's easy enough to design a specification that is safe against double
initialization; but then, it's also not that hard to design a
specification that's safe against super().__init__() and odd
hierarchies.

Since the OP didn't show us any of the code, I had to mention the
possibility here. Of course, it's entirely possible that it isn't
actually a problem.

> In most cases the top object was so abstract that its init()/constructor
> was only doing generic type stuff or opening database sessions/networks
> etc which got lost and tidied up by garbage collectors.

Remember though that diamond inheritance doesn't always happen at the
top-level object.

> So I'm curious about how big this "big problem with MI" is in
> practice. I'm sure there are scenarios where it has bitten folks
> but it never (or very rarely) occurred in our projects. (Of
> course, being maintenance programmers, the problems may have
> been ironed out before the code ever reached us! But that
> wasn't usually the case...)

Who said it's a big problem with MI? Diamond inheritance is in general
a problem to be solved, but calling __init__ twice is a separate
concern and usually only an issue when you try to treat MRO-based MI
as if it were composition-based MI.

ChrisA


More information about the Python-list mailing list