One thing I've never understood about Python 2.x's multiple inheritance
(mostly because I almost never use it) is how you do something like
this:
class Base1(object):
def __init__(self, foo):
self.foo = foo
class Base2(object):
def __init__(self, bar):
self.bar = bar
class Derived(Base1, Base2):
def __init__(self, foo, bar):
# now what???
I need to call __init__() in both of my base classes. I don't see how
super() can do that for me.
It doesn't, and it can't, because your classes are not cooperative. The
problem is, Derived.__init__ needs to call two super methods, but both of
them require different arguments. If this was an ordinary method, you'd
probably see the flaw straight away:
class Artist:
def draw(self, painting): ...
class Gambler:
def draw(self, cards): ...
class GamblingArtist(Gambler, Artist):
def draw(self, painting, cards):
...
Here you've got two completely different actions that merely share the
same name, is it really sensible to have *one* method try to do both? And
if so, you certainly can't expect an automated call to work out which
argument goes to which superclass. So here's a place where cooperative
multiple inheritance doesn't work, and you're reduced to either manually
calling the methods (and hoping that they don't, in turn, end up in some
sort of diamond inheritance diagram, which would be bad), or else you
have to redesign your classes to be more cooperative.
This is one of the reasons why many languages simply prohibit multiple
inheritance outright, except perhaps for mixins, or at least require that
all methods have compatible signatures.
All is not lost, there are ways to make your classes cooperative. The
trick is to have your classes' __init__ methods ignore keyword arguments
they don't know what to do with. object used to do the same thing, but it
no longer does, so you need to add an extra class just before object to
swallow any args before they read object.
class Blocker(object):
def __init__(self, **kwargs):
# Block kwargs from reaching object
super(Blocker, self).__init__()
class Base1(Blocker):
def __init__(self, foo, **kwargs):
self.foo = foo
super(Base1, self).__init__(**kwargs)
class Base2(Blocker):
def __init__(self, bar, **kwargs):
self.bar = bar
super(Base2, self).__init__(**kwargs)
class Derived(Base1, Base2):
def __init__(self, foo, bar):
super(Derived, self).__init__(foo=foo, bar=bar)
This may seem like a lot of work, but nobody said that cooperative
multiple inheritance with different method signatures was easy!
The reality is, multiple inheritance is *hard*. Mixins and traits remove
most of the problems, so if all you've ever used MI for is mixing in new
behaviour, you won't get just how hard it really is. The best way to do
MI is not to do it at all. But if you have to use it, then super is the
only way to do it *right*. Anything else, and you're almost certainly
either duplicating what super would do, or you've got bugs in your code.
Anyway, here are some links about super:
Super considered Super:
http://rhettinger.wordpress.com/2011/05/26/super-considered-super/
Here's a set of three posts, written quite a long time ago but still
relevant, dissecting super in gory detail:
http://www.artima.com/weblogs/viewpost.jsp?thread=236275
http://www.artima.com/weblogs/viewpost.jsp?thread=236278
http://www.artima.com/weblogs/viewpost.jsp?thread=237121
and a more recent update:
http://www.artima.com/weblogs/viewpost.jsp?thread=281127
and a counter-argument, provocatively titled "Super considered Harmful",
although the author backs away from this claim, since it turns out that
the problems are not *super* but multiple inheritance itself.
https://fuhm.net/super-harmful/
Despite telling people not to use super, James Knight doesn't actually
give them a better alternative. I believe this is because he can't --
super is the worst solution to multiple inheritance except for all the
others.