Stephen J. Turnbull writes:

>  I doubt that __as_parent__ solves the "arbitrarily deep"
problem (although you may be able to persuade me it will work
"better" "most of the time").

The way i've implemented it rn, it is possible :
__as_parent__ can take for argument any class in the inheritance tree of the 
class context it is called in:

class An:
  def method(self):
    print('An')
class A[n-1](An): pass
...
class A1(A2): pass
class A0(A1):
  def method(self):
    self.__as_parent__(An).method()

A0().method() prints 'An'

That's what i mean by 'being able to target any ancestors'

On top of that, having to dive into the MRO to pass as argument of super the 
previous class in MRO rather than the one you're targeting feels like 
obfuscation, where no such mental gymnastic is needed with my __as_parent__ 
replacment.


--

> (a) there are
> people who use it for more advanced purposes and (b) it can't cause you
> any confusion in that case.

(a) What are those more advanced feature? On top of the classic inheritance 
features, I can only think of dependency injection in the inheritance tree.
Such a feature could be replaced, although i didn't provide a replacment for 
this feature, it doesn't seem like it's gonna be hard to do, __as_parent__ 
could rely on a dict, which would hold as key a class, and as value a class 
too. If the targeted class is in the keys, target the corresponding value 
instead. This class could then inherit from the class it replaced, and you got 
your dependency injection.
On top of that, it unties a lot more this dependency injection feature from the 
method resolution algorithm.
If you can think of more features that i didn't account for, I wanna hear about 
it.

(b) Wrong! The common use case for inheritance and super is what informs most 
people of its behavior, as of today, this knowledge you acquire turns out to be 
unaplicable in those multiple inheritance scenarios. I mean, it is possible for 
super to call classes on another branch of the inheritance tree. That is most 
definitely *not* what you learn from using super in simple scenarios.
My alternative doesn't do those jumps, and i think overall matches more closely 
what an untrained mind (which none of us here are, don't get blinded by your 
knowledge) would expect.


--


> But C3/super assumes nothing of the kind.

At first i was gonna answer that it of course does make this assumption, but I 
think we aren't exactly talking about the same thing.
I was saying that the assumption that it can make sense to order class 
inheritance trees lead to the use of C3 in python, which it turns out can't 
solve all scenarios. I think this proves that this assumption was wrong.
I think what you are saying is that it doesn't make that assumption because it 
fails to produce an order in some scenarios.

So overall, i think we agree on the matter of fact of today's C3 behavior, and 
overall, i think we agree that C3 is an incomplete solution? correct me if i'm 
wrong, i don't wanna misunderstand you.

The solution i propose would be able to cover those cases C3 doesn't. Because 
it wasn't build around the idea of ordering.


--

> Pragmatically, C3 seems to give useful
> results frequently, unuseful or confusing results infrequently (except
> for people who don't understand C3 and randomly change code, who will
> be confused frequently)

Not knowledgable people should be accounted for to.
Knowledgable people would benefit from an easier to use langage anyways.
But yeah, I agree that the confusing behavior are not in the majority. They 
still exists.


---

> It turns out that this is useful to quite a few Python
> programmers.

No matter what you feed a comunity, some people will make gold out of.
What matters here is that if we change anything, the new world allows what the 
old one did. Feature wise, I mean.

Do we have a list of use case / features provided by the current state of MRO + 
super?
We would wanna keep those behavior in the alternative.
To me, on top of the classic inheritance features, there's only the dependency 
injection in the inheritance tree.
As explained above, it isn't hard to produce an alternative for this feature in 
the realm of my alternative to MRO + super.


--

> Have you specified when the ExplicitResolutionRequired error is raised

I didn't, my apologies
It would be raised at method resolution time.

> (No, I'm not going to read your code for the implementation, I'm
interested in the specification.)

Fine by me, it's just a proof of concept anyways.
Although, I dive into the reason why i came up with this idea and the 
specification, in the README. This might be worth a read.


---


> You can't have "reliability" without disabling super, I suspect, since
> you can't prevent stdlib or 3rd-party code from using it, and I
> believe that means method resolution in that code will depend on MROs,
> even if you can specify the root of the class subtree explicitly.  I
> doubt disabling super would be acceptable to Python core devs, and it
> would very likely make the stdlib less than useful to you.

Yeah, this is essntially the migration problem. It is still an open one to me.
We can think of a few stuff, like adding a flag to be able to switch from one 
feature to the other. Since the simple cases wouldn't change much in their 
apparent behavior, if we introduce my change to super into super, essentially 
running the old super code, or my new version, based on that flag, and using C3 
or my alternative EMR (which for now lives in __getattribute__) could be a way 
to allow people to transition from old super + MRO to __as_parent_ + EMR 
without much pain, at least in cases of simple inheritance.
Idk, i'm just sharing ideas, this might not be the way to handle the 
introduction of that feature / migration if that's the goal


--

> I'm not sure I like "reliability" as a requirement.  The point of MRO
> is that it is an attribute of the object

Well, it *has* to be a class attribute for it to work.
my alternative can lie in __getattribute__, no need for an extra class 
attribute.
Obviously, it feels weird to get rid of it, because today mro is so unobvious 
that having it has a class attribute is a must.
but my solution just doesn't need. Actually such an attribute would make no 
sense in my alternative.


--

> But with a "reliable"
> __as_parent__, it's possible that a method defined in the child calls
> __as_parent(SmallestFirst).my_sort(), while a method in the parent
> calls __as_parent(LargestFirst).my_sort().  This may be a problem

I'm sorry, i completely fail to understand what you mean, can you provide a 
code example to illustrate what you mean?


---

> I don't understand "ancestors as targets".  If there's a name
> collision between grandparents, that will already raise
> ExplicitResolutionRequired.  So a child of those grandparents must use
> __as_parent__, which resolves the problem in the default case.  Do you
> just mean that if for this particular grandchild, you don't want the
> default resolution, you can use __as_parent__(grandparent_a)?

Yes.
Since ExplicitResolutionRequired are raised only when resolving the method, 
grandparents can both present a method with the same name, which the parent 
would not necesserly redefine to resolve the conflict, if it doesn't intent on 
calling it. The child in this case would be stuck if 
__as_parent__(grandparent_a) was not an option


> If the error is raised at
> method resolution time, what happens if you never try to resolve it?
> How about a case where the parent doesn't specify a resolution of a
> collision between grandparents, but no code tries to resolve that
> method?  Shouldn't the child be allowed to use __as_parent__ there?

If you never try to resolve it, you don't get an error.
When a parent doesn't specify a resolution for a collision in grandparents, the 
child is allowed to call __as_parent__ on the grandparent (it is allowed to do 
that in any case).
Actually, it would need to, if it intend on resolving that method at some point 
to get out of the ExplicitResolutionRequired  error


---

> I don't much like your definition of "conflict".  Mine is "when
> super() does the wrong thing", which (surprise!) it almost never does
> for me, and I've never used the two-argument form (except in my other
> post to you): class.method calls have always been adequate.  I've
> never seen a case where I couldn't diagnose the need to override the
> MRO from the class declaration.  Not saying problem cases don't exist,
> just that I'm perfectly happy with C3 MRO and super().

I think what i refer to as conflict is what you refer to as collision.
Essentially, when two parent class provide a method with the same name, that's 
what i call a conflict.

I agree that most scenarios are not problematic, essentially because most 
scenarios don't involve multiple inheritance. And that's why i made sure the 
external behavior of my alternative matches today's external behavior of super 
+ MRO in those cases.
Idk how often you are confronted to multiple inheritance, I personnaly am 
fairly often, for example with class based views in django, and It is not 
uncommon to have to place a mixin before the View class we wanna inherit from.
A possible reason is that the View class has colliding methods with the mixin 
(maybe simply __init__) in which they don't do a call to super, as they are at 
the top part of the inheritance tree.
Which in term, completely lose a branch of the inheritance tree __init__.
When designing View, who can blame them for not thinking of that?

On top of that, if the collision is not on a dunder method, adding a call to 
super in the top parent class would raise an error when called outside multiple 
inheritance, as its parent don't have those method, but in case of multiple 
inheritance, it's needed.
How are anyone supposed to solve for that?

I also wanna point out that most of us here are very familiar with the current 
state of MRO + super, and our easiness to work with it could show that we are 
knowledgable as much as it could show that the feature is easy to use. But it 
doesn't distinguish between those two explanation, so i'm not confortable with 
this argument.
_______________________________________________
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/L2KXIO2W5KOFUSBJEEIMUAGTFRBFFYYZ/
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to