super() and multiple inheritance

hermy herman at kuleuven.net
Thu Dec 1 16:08:52 EST 2005


Carl Banks schreef:

> hermy wrote:
> > Hi,
> > I'm trying to figure out how to pass constructor arguments to my
> > superclasses in a multiple inheritance situation.
> >
> > As I understand it, using super() is the preferred way to call
> > the next method in method-resolution-order. When I have parameterless
> > __init__ methods, this works as expected.
> > However, how do you solve the following simple multiple inheritance
> > situation in python ?
> >
> > class A(object):
> >      def __init__(self,x):
> >          super(A,self).__init__(x)
> >          print "A init (x=%s)" % x
> >
> > class B(object):
> >      def __init__(self,y):
> >          super(B,self).__init__(y)
> >          print "B init (y=%s)" % y
> >
> > class C(A,B):
> >      def __init__(self,x,y):
> >          super(C,self).__init__(x,y)  <-------- how to do this ???
> >          print "C init (x=%s,y=%s)" % (x,y)
> >
> > What I want is that when I create a class C object
> >     x = C(10,20)
> > that the x argument of C's __init__ is used to initialize the
> > A superclass, and the y argument is used to initialize the B
> > superclass.
> > In C++, I would do this using initilaization lists, like:
> >     C::C(int x, int y) : A(x), B(y) { ... }
>
> Well, technically, you can't do this in C++ at all.
>
> A little explanation.  In multiple inheritance situations, Python has a
> significant difference from regular C++ inheritance: in C++, if you
> multiply-inherit from two different classes that both inherit from the
> same base class, the resulting structure has two copies of the data
> associated with the base class.  In Python, only there is only one
> copy.  If you want only one copy of the base class's data in C++, you
> must use virtual inheritance.
>
> But here's the thing: in C++, you can't initialize a virtual base class
> in the constructor.  A virtual base class is always initialized with
> the default constructor.  The reason for this is obvious: otherwise,
> you could end up initializing the virtual base twice with different
> arguments.
>
> This also explains why, in Python, super is preferred for multiple
> inheritance: it guarantees that each base class's __init__ is called
> only once.  This comes at the price of less flexibility with the
> function arguments, but in Python, at least you can use function
> arguments.
>
> So now, let's talk about solutions.
>
> Now that we know why super is preferred, we can make a somewhat
> intelligent decision whether to go against the advice.  If you know
> your inheritance hierarchy is not going to have any two classes
> inheriting from the same base class (except for object), then you could
> just call each class's __init__ directly, same as you would have done
> with old-style classes.  There is no danger of initializing any base
> class twice and no reason for super to be preferred here.
>
>   A.__init__(self,x)
>   B.__init__(self.y)
>
> But if you can't or don't want to do this, you'll have to make some
> concessions with the argument lists.  One thing to do would have A and
> B both accept x and y, using only the one it needs.  A more general
> approach might be to use keyword arguments.  For example (you can
> improve upon this):
>
>   class A(object):
>       def __init__(self,**kwargs):
>           use(kwargs['x'])
>
>   class B(object):
>       def __init__(self,**kwargs):
>           use(kwargs['y'])
>
>   class C(A,B):
>       def __init__(self,**kwargs):
>           super(C,self).__init__(**kwargs)
>
>   C(x=1,y=2)
>
>
> > I'm probably overlooking some basic stuff here,
>
> Unfortunately, it doesn't appear that you are.  You'll have to choose
> between calling base class __init__s old-style, or fiddling with their
> argument lists.
>
>
> Carl Banks

Thanx, I think I got it (please correct me if I'm wrong):
o super(C,self) determines the next class in the inheritance hierarchy
according to
  method resolution order, and simply calls the specified method on it
(in this case
 __init__ with the specified argument list.
o since I cannot now beforehand where my class will appear in the
inheritance
  hierarchy when __init__ is called, I need to pass on all the
arguments and let
  my method decide which ones to use.

On the other hand, when I use old-style, s.a. B.__init__(args), I
specify statically
in which class the method lookup should occur.

Unfortunately, new and old style don't mix well (as I found out by
experimenting a little),
so for new code I should probably stick to new style, and use super.

Which leads me to my original problem. Your suggestion with **kwargs
works fine,
but only if it's used consistently, and I'll probably need to do some
name-mangling
to encode class names in parameter names in order to avoid name
clashes.

Unfortunately, this solution is (as far as I know) not universally
accepted, so if I want
to multiply inherit from other people's classes (who didn't follow this
solution), it won't
work. Short: I can make it work if I write all the classes myself, I
can't make it work if
I try to re-use other people's code.
Which is a pity, since this means that I can't even use multiple
inheritance for mixin
classes (where I know that no diamond will appear in the hierarchy, and
I have a simple
tree - except for the shared object superclass).

So, for the moment my conclusion is that although Python has some
syntax for
multiple inheritance, it doesn't support it very well, and I should
probably stick to
single inheritance.
Also, if there would be some language support for the argument passing
problem
(especially for __init__ methods), multiple inheritance would work just
fine. Hopefully
some future version of Python will solve this.

regards,
herman




More information about the Python-list mailing list