
Eric V. Smith writes:
I've said this before, but I'll repeat it here: if your proposal is to have super().something raise an error if "something" exists on more than one parent class (via the MRO), then that's a breaking change and it will not be accepted. The expliciteness required error should be raise at attribute / method access, not through super. My proposal would completely detach method resolution from calls to super. essentially:
class A:
val = 1
class B:
val = 2
class C(A,B): pass
C.val # raises an explicitness required error
It's fair to assume super would need to know what parent class it should target however, especially since part of my proposal is to make it so that super can take as an argument the class it targets, instead of the previous in MRO order, but that is not something i consider an "active" part of my proposal (the idea that super needs to be passed a parent as argument in case of MI). As i was answering Ronald Oussoren, i figured that in case of MI, a call to super with no argument could simply loop over call to super with each parent, one after the other. That would keep it a nice way to tell "go fetch all parents behaviors" without having to write down one line per parent. I think it would behave 'strictly' as today's super calls, except in cases where one class appears multiple time in the inheritance tree (and is visited through calls to super). For those scenarios, i have a dedicated answer. Your exemple actually is one of those cases, since calls to super do visit the object parent class. Let's start with super() implicitelly calling super on all parents one after the other. ``` class A: def foo(self): print("A") try: super().foo() except AttributeError: print('no parent defines foo') class B: def foo(self): print("B") try: super().foo() except AttributeError: print('no parent defines foo') class C(A, B): def foo(self): print("C") super().foo() C().foo() ``` This would output (not accounting for the parent appearing multiple time proposal) : ``` C A no parent defines foo B no parent defines foo ``` in this case, C would be equivalent to, (but not require any rewrites): ``` class C(A, B): def foo(self): print("C") super(A, self).foo() super(B, self).foo() # keep in mind, in my proposal, the class passed as an argument is the one we target. ``` I occasionally write it down without the self, or with self.__as_parent__(..) but overall, i'm fine with super(class, self), as long as the class is the target, not the previous to the target in MRO order. You get the idea. If accounting for the parent appearing multiple time (i'll just call it the diamond case for now) my proposal for those is to have a module strats, that would allow to decide what occurence of a reoccuring parent should be visited or not. the default strat would be 'last', as to match the current behavior. Idk exactly what API is the better for this proposal, but since the goal is for it to default to today's behavior, we wouldn't need to write down anything related to it in this scenario, we would get that output ``` C A B no parent defines foo ``` Which is the one you want. TLDR: with my full proposal, the code you presented me won't break. It won't even change behavior at all. Or in other term, I acknowledge breaking changes are bad, but they're not impossible to cover for. And i think i have covered for it fairly extensively. If you think you can come up with code that would break under my proposal, hit me. I wanna have the least amount of breaking change. And as much as possible, i wanna cover for those breaking changes, to find a way to update my proposal, so those aren't breaking changes anymore. One thing i can think of rn is that calls to super in classes without parent would turn out to break with my proposal, since they would now refer to object anytime. That needs covering up too, and i'll be thinking on that one too. Like i've done for every scenario i've been opposed with so far, i think.