On Sun, Apr 03, 2022 at 10:06:35PM -0000, malmiteria  wrote:

> Then what's the solution to the diamond problem in python?
> in this example : 

[code removed for brevity]

class hierarchy:

```
                 HighGobelin
                    /   \
                   /     \
                  /       \
        ProudGobelin    CorruptedGobelin
                  \       /
                   \     /
                    \   /
                   HalfBreed
```

By the way, you have a bug in your use of random. You call 
random.choices, which returns a list which is always true, and so only 
the super(HalfBreed, self) branch gets called.

You should call random.choice (not plural). Also you misspelled 
CorruptedGobelin.


> You want calls to ProudGobelin scream method to visit ProudGobelin, then 
> HighGobelin

And indeed that is exactly what happens when you call ProudGobelin's 
scream method:

>>> ProudGobelin().scream()
I ... can't ... contain my scream!
raAaaaAar


This makes sense: ProudGobelin's class heirarchy is plain old single 
inheritence with no diamond, so there is absolutely no reason to expect 
it to call anything except its own method and that of its parent(s).

And it doesn't. Python works perfectly fine here.


> Not ProudGobelin, then CorruptedGobelin, then HighGobelin.

This does not happen when calling ProudGobelin's method.

But when you call HalfBreed's scream method, after fixing the two typos, 
again we get the correct behaviour. Although it might be *unexpected* if 
you don't understand super().


* Half the time you use super(HalfBreed, self), and we get the 
  expected behaviour. Because HalfBreed is part of a diamond, it should 
  call *both* immediate parents as well as the grandparent, and it does:


>>> HalfBreed().scream()
I ... can't ... contain my scream!
my corrupted soul makes me wanna scream
raAaaaAar


* and the other half the time, you use super(ProudGobelin, self), but 
  this time the behaviour is unexpected, because you don't realise that
  super(ProudGobelin) does **not** mean "call ProudGobelin's method".


>>> HalfBreed().scream()
my corrupted soul makes me wanna scream
raAaaaAar


You get *CorruptedGobelin*'s output instead of ProudGobelin. This is not 
a bug in super, it is a bug in your understanding of super.

To get the effect you want, you should not use super, but call the 
method you actually want:

    ProudGobelin.scream(self)

I am not an expert in super(), but I *think* that the call to super here:

    super(ProudGobelin, self).scream()

looks at self's MRO: 

    [HalfBreed, ProudGobelin, CorruptedGobelin, HighGobelin]

and skips straight to the call that would occur *after* ProudGobelin, 
which of course is CorruptedGobelin. Because the instance self is still 
a HalfBreed, it uses the HalfBreed MRO, not the ProudGobelin MRO.

What is the class that follows ProudGobelin in the HalfBreed MRO? It is 
CorruptedGobelin, so of course that is the method that will get used.

The same thing occurs without the diamond shape. Remove CorruptedGobelin 
from HalfBreed, but keep the rest of the code the same:


```
class HalfBreed(ProudGobelin):  # No diamond here.
    def scream(self):
        if random.choice([True, False]):
            print("Halfbreed --")
            super(HalfBreed, self).scream()
        else:
            print("ProudGobelin --")
            super(ProudGobelin, self).scream()
```

(I have added extra print statements so we can see which branch is 
taken.) You will see that super(ProudGobelin, self) goes to the *next* 
class in HalfGobelin's MRO, which is HighGobelin.

Which is of course the right behaviour, if you think about how it must 
work. Otherwise you would have an infinite recursion.

The TL;DR here is that you mistakenly expected:

    super(ProudGobelin, self).scream()

to be the same as ProudGobelin.scream(self), but it is not. It is the 
same as self.scream(), using self's MRO (HalfGobelin), but called from 
ProudGobelin's method, which means it goes on to the next class in 
HalfGobelin's MRO *after* ProudGobelin.



-- 
Steve
_______________________________________________
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/DUZ7UHIZEG3NH34I3SDATVZSOGEY2R6S/
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to