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

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