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