
Stephen J. Turnbull writes:
It may not be flexible, but it is quite useful. In most cases it's quite trivial to pick up the version from the second parent:
class C(A, B): def foo(self, *args): return B.foo(self, *args) # we don't define bar() because we want C to inherit A's # version This exemple shows, a dissymmetry between foo and bar, in cases A and B share a parent P. foo method only visits C, B then P bar visits C, A, B, P You'd need to return A.bar in C bar methods to get more symmetry, but what if you wanted foo to visit the inheritance tree in this order? C, B, A, P. (While having bar visiting them in this order C, A, B, P) How would you go about it?
The questions are
1. Are there any "!#$%ing Python" cases that cannot be worked around? 2. How frequent are the "!#$%ing Python" cases compared to the Just Works cases? 3. How painful are the workarounds? Actually, there's more questions to be asked. I would add:
- Is there any problems at any steps of the programming experience (producing a feature, debugging, testing, refactoring, logging, and so on) I would argue the debugging experience in multiple scenarios i've presented are horrible today, some refactoring aren't possible (more on that later), and overall, the problem is the learning curve. - For those problems, are the solution accessible to everyone? Not everyone knows where to find docs, not everyone who finds docs knows how to read it, not everyone who knows how to read it will understand it, and so on. For the refactoring example, i believe it to be a good answer to your "Are there any "!#$%ing Python" cases that cannot be worked around?" Assume you're a lib author. You're providing a few classes, let's say about cook's styles. Each class has multiple methods, clean, slice, dice, mince, and so on. Your lib turned out quite successful, and you've been requested to add a few more cook's styles. While adding those, you realise you're repeating a lot of the clean method, but not all of it. So, you refactor it, as you now identify a few cleaning styles. You start from something like that: ``` class FrenchCook: def clean(self): print("baguette") print("fromage") print("european cleanning style") class GermanCook: def clean(self): print("beer") print("european cleanning style") ``` And end up with something like this. ``` class EuropeanCleaning: def clean(self): print("european cleanning style") class FrenchCook(EuropeanCleaning): def clean(self): print("baguette") print("fromage") super().clean() class GermanCook(EuropeanCleaning): def clean(self): print("beer") super().clean() ``` Do you see the problem with this refactoring? Keep in mind that your lib is used a lot, so any API change won't be possitively received by your lib users. Meaning composition is not an option. If you can't see the problem with this refactoring, that's my point, because there's one. If you can see the problem with this refactoring, good for you. The problem is still there. Is there a refactoring that wouldn't exhibit this problem, yes. This solution has it's own issues, that i'll talk about once you answer me. i wouldn't wanna spoil the exercice by giving you the answer. Actually, this could be one question i'll add into the form i'm getting ready. I'll give it to you all so we can agree on the fairness of the questions first, and stuff like that, before running it.
That doesn't "look like" a mixin, though. That looks like a simple inheritance chain Our experiences with mixins seems very different, i've consistently seen them used as a way to "extend" the not mixin class behavior. The adoption syntax i propose *is* a simple inheritance chain (in this exemple), and that's the goal. Mixins in your experience seems to be a lot more 'compositional' and for them, my proposal wouldn't change much. They would still be used the same way. There would be one major difference, which is that when multiple mixins provide the same attribute / method, accessing this attrribute / method would raise an ExplicitResolutionRequired error, instead of leaving it be just like that. That would reduce the risk of missing that possible error, and make the error actually related to the source of the error. Solutions to fix this errors aren't hard to provide : either mixins need to rename their attributes / method with a double underscore at first, or the mixin user can just redefine the method / select which to use, by assigning the mixin.method to the class method.