The way super is intended to be used is as a way to provide a single linear chain of super calls which traverses through the inheritance hierarchy.
The order of the single chain it uses is the same one used for method resolution order - so to see it, use inspect.getmro. In your case, calling import inspect inspect.getmro(C) gives: (<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <type 'object'>) (If you want to know the nitty-gritty details of how python goes about determining the mro, look here: http://www.python.org/download/releases/2.3/mro/ ) The super call: super(Foo, Bar) can then be thought of conceptually as 'using the mro of Bar, find the next object up the chain from Foo'. Which explains why calling super(A, self) in your case called the init of B: self was an instance of C, and the mro for C is (C,A,B,object) - and, using that mro, the next class up from A is B. Unfortunately, your solution is not a general one, because you're building in an assumption about the mro into C.__init__ - namely, that the next object in the inheritance chain after C will always be A - and this is not always the case. Consider this bit of code: from pprint import pprint import inspect class A(object): def __init__(self): print 'A instantiated' self.important = 'a' class B(object): def __init__(self): print 'B instantiated' self.important = 'b' self.bIsImportant = 'too' class C(A, B): def __init__(self): print 'C instantiated' super(C, self).__init__() super(A, self).__init__() class D(A, B): def __init__(self): print 'D instantiated' super(D, self).__init__() super(A, self).__init__() class E(C, D): def __init__(self): print 'E instantiated' super(E, self).__init__() pprint(inspect.getmro(E)) print E() The classes A,B,C are all identical to yours, except I've removed the unnecessary pass statements, and added an info print to C.__init__. With class D, I've followed your example of A for inheriting from (A,B). Then E inherits from C,D. When you run it, we get this: mro C: (<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <type 'object'>) making C: C instantiated A instantiated B instantiated mro D: (<class '__main__.D'>, <class '__main__.A'>, <class '__main__.B'>, <type 'object'>) making D: D instantiated A instantiated B instantiated mro E: (<class '__main__.E'>, <class '__main__.C'>, <class '__main__.D'>, <class '__main__.A'>, <class '__main__.B'>, <type 'object'>) making E: E instantiated C instantiated D instantiated A instantiated B instantiated B instantiated C and D seem to instantiate correctly.. but when you try to create E, you hit problems. B is instantiated twice. Why? Well, the assumption we were making when we called super(C,self) then super(A,self) was that super(C,self) would yield A... then super(A,self) would give the next item in the chain after A. But the mro for E is (E,C,D,A,B) - so the assumption that super(C,self) is A is now invalid. The correct way to hit every element in the inheritance chain when using super is to have every element in the super chain call super - even the ones that inherit from object... because, even though by themselves their super will simply be object, and the super call will be pointless, with multiple inheritance, the super may NOT be object, but some other class in the mro chain. ie, if we change the definitions of A, B, C, and D to call their super once and exactly once: class A(object): def __init__(self): print 'A instantiated' self.important = 'a' super(A, self).__init__() class B(object): def __init__(self): print 'B instantiated' self.important = 'b' self.bIsImportant = 'too' super(B, self).__init__() class C(A, B): def __init__(self): print 'C instantiated' super(C, self).__init__() class D(A, B): def __init__(self): print 'D instantiated' super(D, self).__init__() ...then everything works. The reason it works is that super (ie, the mro) provides a single, linear chain through the inheritance heirarchy... so if every method calls it's super, we will hit every class in the hierarchy once, and only once. Unfortunately, we still have potential problems - ie, what if we want our __init__ to take arguments, and not all the class's inits take the same number of arguments? This is explained in far more detail here: http://fuhm.net/super-harmful/ The basic skinny is that, when using super + multiple inheritance, it's best to use *args/**kwargs in ALL the methods that are using super. Even then though, you may run into problems - for instance, the example he gives is when trying to use super and __new__... the problem being that object.__new__ never accepts any args/kwargs. Really, what I tend to take out of all this is: Multiple Inheritance is very tricky Use it only when necessary When you do use it, try to make your inheritance schemes as as simple as possible, and be very, very careful. ;) - Paul On Wed, Dec 22, 2010 at 11:56 AM, Viktoras <[email protected]> wrote: > class C(A, B): > >> def __init__(self): >> super(C, self).__init__() >> super(A, self).__init__() >> pass >> > > unless you have a reason not to hardcode parent classes in the constructor, > i think all you need is just not using super() at all: > > def __init__(self): > A.__init__(self) > B.__init__(self) > pass > > > > > -- > http://groups.google.com/group/python_inside_maya > -- http://groups.google.com/group/python_inside_maya
