On Jan 5, 2005, at 6:36 PM, Guido van Rossum wrote:
The idea of calling both __init__ methods doesn't work if there's a diamond; if there *is* a diamond (or could be one), using super() is the only sane solution.
So then don't use it. You couldn't have diamonds at all before 2.2. With *care* and *understanding* you can do the right thing in 2.2 and beyond.
I'm getting tired of super() being blamed for the problems inherent to cooperative multiple inheritance. super() is the tool that you need to solve a hairy problem; but don't blame super() for the problem's hairiness.
Please notice that I'm talking about concrete, real issues, not just a "super is bad!" rant. These are initially non-obvious (to me, at least) things that will actually happen in real code and that you actually do need to watch out for if you use super.
Yes. It is a hard problem. However, the issues I talk about are not issues with the functionality and theory of calling the next method in an MRO, they are issues with the combination of MROs, the implementation of MRO-calling in python (via "super"), and current practices in writing python code. They are not inherent in cooperative multiple inheritance, but occur mostly because of its late addition to python, and the cumbersome way in which you have to invoke super.
I wrote up the page as part of an investigation into converting Twisted to use super. I thought it would be a good idea to do the conversion, but others told me it would be a bad idea for backwards compatibility reasons. I did not believe, at first, and conducted experiments. In the end, I concluded that it is not possible, because of the issues with mixing the new and old paradigm.
If you have a framework with classes written using the old paradigm that a subclass must call the __init__ (or frob) method of each of its superclasses, you can't change your framework to use super() instead while maintaining backwards compatibility.
Yep, that's what I said, too.
If you didn't realize that before you made the change and then got bitten by it, tough.
Luckily, I didn't get bitten by it because I figured out the consequences and wrote a webpage about them before making an incorrect code change.
Leaving behind the backwards compatibility issues...
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, and 2) that traditionally __init__ is called with positional arguments.
To fix #1, it would be really nice if you could write code something like the following snippet. Notice especially here that the 'bar' argument gets passed through C.__init__ and A.__init__, into D.__init__, without the previous two having to do anything about it. However, if you ask me to detail how this could *possibly* *ever* work in python, I have no idea. Probably the answer is that it can't.
class A(object): def __init__(self): print "A" next_method
class B(object): def __init__(self): print "B" next_method
class C(A): def __init__(self, foo): print "C","foo=",foo next_method self.foo=foo
class D(B): def __init__(self, bar): print "D", "bar=",bar next_method self.bar=bar
class E(C,D): def __init__(self, foo, bar): print "E" next_method
class E2(C,D): """Even worse, not defining __init__ should work right too."""
E(foo=10, bar=20) E2(foo=10, bar=20) # Yet, these ought to result in a TypeError because the quaz keyword isn't recognized by # any __init__ method on any class in the hierarchy above E/E2: E(foo=10, bar=20, quaz=5) E2(foo=10, bar=20, quaz=5)