[Python-Dev] Re: super() harmful? (original) (raw)

James Y Knight foom at fuhm.net
Thu Jan 6 08:46:11 CET 2005


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.

Very true.

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)

James



More information about the Python-Dev mailing list