On Sun, 27 Mar 2022 at 23:03, malmiteria <martin.mi...@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
_______________________________________________
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/X5J2625EYC7XELZUCQOM4GGJARV5IPEJ/
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to