On Jan 6, 2005, at 12:13 PM, Guido van Rossum wrote:
So it has nothing to do with the new paradigm, just with backwards compatibility. I appreciate those issues (more than you'll ever know) but I don't see why you should try to discourage others from using the new paradigm, which is what your article appears to do.
This is where I'm coming from: In my own code, it is very rare to have diamond inheritance structures. And if there are, even more rare that both sides need to cooperatively override a method. Given that, super has no necessary advantage. And it has disadvantages. - Backwards compatibility issues - Going along with that, inadvertent mixing of paradigms (you have to remember which classes you use super with and which you don't or your code might have hard-to-find errors). - Take your choice of: a) inability to add optional arguments to your methods, or b) having to use *args, **kwargs on every method and call super with those. - Having to try/catch AttributeErrors from super if you use interfaces instead of a base class to define the methods in use.
So, I am indeed attempting to discourage people from using it, despite its importance. And also trying to educate people as to what they need to do if they have a case where it is necessary to use or if they just decide I'm full of crap and want to use it anyways.
In order to make super really nice, it should be easier to use right. Again, the two major issues that cause problems are: 1) having to declare every method with *args, **kwargs, and having to pass those and all the arguments you take explicitly to super,
That's only an issue with __init__ or with code written without cooperative MI in mind. When using cooperative MI, you shouldn't redefine method signatures, and all is well.
I have two issues with that statement. Firstly, it's often quite useful to be able to add optional arguments to methods. Secondly, that's not a property of cooperative MI, but one of cooperative MI in python.
As a counterpoint, with Dylan, you can add optional keyword arguments to a method as long as the generic was defined with the notation #key (specifying that it will accept keyword arguments at all). This is of course even true in a single inheritance situation like in the example below.
Now please don't misunderstand me, here. I'm not at all trying to say that Python sucks because it's not Dylan. I don't even particularly like Dylan, but it does have a number of good ideas. Additionally, Python and Dylan differ in fundamental ways: Python has classes and inheritance, Dylan has generic functions/multimethods. Dylan is (I believe) generally whole-program-at-a-time compiled/optimized, Python is not. So, I think a solution for python would have to be fundamentally different as well. But anyways, an example of what I'm talking about:
define generic g (arg1 :: <number>, #key); define method g (arg1 :: <number>, #key) format-out("number.g\n"); end method g;
define method g (arg1 :: <rational>, #key base :: <integer> = 10) next-method(); format-out("rational.g %d\n", base); end method g;
define method g (arg1 :: <integer>, #key) next-method(); format-out("integer.g\n"); end method g;
// Prints: // number.g // rational.g 1 // integer.g g(1, base: 1);
// Produces: Error: Unrecognized keyword (base) as the second argument in call of g g(1.0, base: 1);
Cooperative MI doesn't have a really good solution for __init__. Defining and calling __init__ only with keyword arguments is a good solution. But griping about "traditionally" is a backwards compatibility issue, which you said you were leaving behind.
Well, kind of. In my mind, it was a different kind of issue, as it isn't solved by everyone moving over to using super. As nearly all the code that currently uses super does so without using keyword arguments for __init__, I considered it not so much backwards compatibility as a re-educating users kind of issue, the same as the requirement for passing along all your arguments.
Exactly. What is your next_method statement supposed to do?
Well that's easy. It's supposed to call the next function in the MRO with _all_ the arguments passed along, even the ones that the current function didn't explicitly ask for. I was afraid you might ask a hard question, like: if E2 inherits C's __init__, how the heck is it supposed to manage to take two arguments nonetheless. That one I *really* don't have an answer for.
No need to reply except when you've changed the article. I'm tired of the allegations.