Great post Paul !


On Thu, Dec 23, 2010 at 7:00 PM, Paul Molodowitch <[email protected]>wrote:

> 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
>



-- 
David Moulder
http://www.google.com/profiles/squish3d

-- 
http://groups.google.com/group/python_inside_maya

Reply via email to