
On Sun, 27 Mar 2022 at 23:03, malmiteria <martin.milon@ensc.fr> wrote:
Actually, no, it does not. Only the A.method() runs, because A was not designed for multiple-inheritance. C inherits from both A and B, but only calls one of the methods.
Yeah, my point was to say that no error is raised, but that the behavior of the program is not really what you'd expect, so yeah.
I'm not sure what you DO expect, though. You're making the assumption here that nobody expects the Spanish resolution order, but I think my experience with Python is very different from yours, so you're going to have to go into a bit more detail about what you expect here. One of the reasons to use the MRO that Python currently does is that it is perfectly possible and plausible to change the MRO by subclassing again. That is to say, even if class C's MRO is (C, B, A, object), you could create a class D which has an MRO of (D, C, Mixin, B, A, object), which would then allow D to affect the behaviour of C's super() calls. How does your proposal handle this? What is its definition of "conflict"? In proper cooperative multiple inheritance, there's no such thing as a conflict; as the programmer, you choose which methods override and which methods augment, simply by creating those methods and choosing whether to call super's method or not.
Essentially, i have a problem with code behavior being unexpected, and my proposal would add an error here, to attract the attention of the developper, for them to solve that problem.
I'm very unclear on what constitutes an error. Simply having the same method name in multiple parents is most definitely NOT an error, as that's the entire point of inheritance and super.
And yeah, A is not designed for multiple inheritence. Today it would have to be designed for it, but that's (one of) my problem here, why would a parent class need to know about its childs, it seems like a poorly attribute responsibility, especially since this child can have other parents, and its behavior can't be fully known from A.
Class A shouldn't have to care what other parents its children have. (Which, I have to say, would make Thanksgiving dinner an extremely awkward time.) For example, here's a cut-down version of (more-or-less) how one of my brothers' projects uses inheritance: class Channel(Gtk.Frame): def __init__(self): ... bunch of GTK stuff, including setting up ... ... events that will call self.refract_value(x) ... def refract_value(self, value): self.update_position(value) self.update_target(value) def update_position(value): pass def update_target(value): pass Normally, a subclass of Channel just has to provide those last two functions, overriding the stubs. But if it needs to make a small adjustment somewhere else, it can. And it's perfectly reasonable to have a Channel subclass that has full functionality, and then subclass *that* class to make a small tweak to the behaviour, by overriding some specific method. The behaviour cannot be fully known from the Channel class, but you know what? It doesn't need to. All refract_value has to know is: there are methods called update_position and update_target, and I call those. The purpose of those methods is consistent, and their function signatures are consistent, so it doesn't need to care exactly which subclass is providing them. And if a mixin class overrides refract_value to add a third place to send the value, that's absolutely fine! It would look something like this: class ChannelLogger(Channel): def refract_value(self, value): self.log_value(value) super().refract_value(self, value) def log_value(value): ... And now, any class can inherit from (ChannelLogger, SomeOtherChannel) to augment the behaviour of an existing class. So where, in this hierarchy, is the "error" that needs to be reported? Which of these super() calls would, by your description, need to raise an error? It's far less obvious than you perhaps think, so please elaborate, please show exactly what constitutes an error.
Okay, i wasn't clear enough, my bad ``` class A: def call_me_in_A_first(self): # calls super def call_me_in_B_first(self): # calls super
class B: def call_me_in_A_first(self): # calls super def call_me_in_B_first(self): # calls super
class C(A,B): def call_me_in_A_first(self): # calls super def call_me_in_B_first(self): # calls super ```
Today, super locks you in the C A B order, even if that is not the order you want
If that's not the order you want, don't define "class C(A, B)". I honestly cannot imagine a situation in which you would create this scenario, but if you did, maybe one of A and B should be broken into two classes. It seems like you're trying to augment superclass behaviour in a very complicated way, but if you did something like "class C(B1, A, B2)", you could have some parts of what's currently in B happen before A, and other parts happen after A.
A solution in some cases would be to reorder the parent in the class definition of C This scenario here highlights a case when such a workaround is not enough Obviously you can still use the class.method syntax, but that's the problem : the current super feature doesn't work here
My alternative to super would, since you can just pass it an argument telling what parent it should target, it would look like that
``` class A: def call_me_in_A_first(self): # don't have to calls super def call_me_in_B_first(self): # don't have to calls super
class B: def call_me_in_A_first(self): # don't have to calls super def call_me_in_B_first(self): # don't have to calls super
class C(A,B): def call_me_in_A_first(self): __as_parent__(A).call_me_in_A_first() __as_parent__(B).call_me_in_A_first() def call_me_in_B_first(self): __as_parent__(B).call_me_in_B_first() __as_parent__(A).call_me_in_B_first() ```
What happens if I create these classes? class D(B): ... class E(C, D): ... What is your __as_parent__(A) and __as_parent__(B) behaviour here? The MRO for E would be (E, C, A, D, B, object). Presumably D is a modified version of B and should be augmenting its behaviour. How is __as_parent__ supposed to handle this?
You can't just inherit from arbitrary classes that don't work together. "Uncooperative multiple inheritance" is an unsolvable problem, and is best refactored using composition instead.
It isn't solved today, and that's the point of my alternative to MRO and my alternative to super. Although i'm not sure exactly how you define "uncooperative multiple inheritence" But this seems to be solvable by raising error on conflicting names, right?
I mean, it's not so different from a conflict when merging a git branch into another one, i'll explain that more in an answer down below, i'm trying to answer every one as much as i can in a timely manner
More broadly, I would say that, in any class hierarchy, the pinnacle class (Channel in my above example) needs to define the protocol, and every other class needs to follow that protocol. With that established, the MRO is your way to control which ones have priority, and you should never need to explicitly select a parent, since augmentation can happen at any point in the hierarchy. If you have a class that doesn't follow protocol, it is broken. Maybe more real-world-ish examples would help? All the toy examples that say "this needs to happen before that" are a bit too trivial to be clear, since it's highly unobvious what things would be provided where in a more realistic hierarchy. ChrisA