Implicitly call parent class initializer

class A(object): def __init__(self, a, b, c): #super(A, self).__init__() super(self.__class__, self).__init__() class B(A): def __init__(self, b, c): print super(B, self) print super(self.__class__, self) #super(B, self).__init__(1, b, c) super(self.__class__, self).__init__(1, b, c) class C(B): def __init__(self, c): #super(C, self).__init__(2, c) super(self.__class__, self).__init__(2, c) C(3) 

In the above code, the commented __init__ calls are displayed as a common โ€œsmartโ€ way to do superclassical initialization. However, in the event that the class hierarchy is likely to change, until recently I used an unfinished form.

It seems that in the super constructor call for B in the above hierarchy, that B.__init__ is called again, self.__class__ is actually C , not B , as I always assumed.

Is there some way in Python-2.x that I can maintain proper MRO (regarding initializing all parent classes in the correct order) when calling superconstructors without naming the current class ( B in in super(B, self).__init__(1, b, c) )?

+7
python super constructor method-resolution-order
source share
4 answers

Short answer: no, there is no way to implicitly call the correct __init__ with the correct arguments of the right parent class in Python 2.x.

By the way, the code as shown here is incorrect: if you use super (). __init__ , then all classes in your hierarchy must have the same signature in their __init__ methods. Otherwise, your code may stop working if you introduce a new subclass that uses multiple inheritance.

See http://fuhm.net/super-harmful/ for a more detailed description of the problem (with images).

+3
source share

Are you looking for metaclasses?

 class metawrap(type): def __new__(mcs,name, bases, dict): dict['bases'] = bases return type.__new__(mcs,name,bases,dict) class A(object): def __init__(self): pass def test(self): print "I am class A" class B(A): __metaclass__ = metawrap def __init__(self): pass def test(self): par = super(self.bases[0],self) par.__thisclass__.test(self) foo = B() foo.test() 

Print "I am Class A"

What the metaclass does is overrides the initial creation of class B (not the object) and ensures that the built-in dictionary for each object B now contains an array of bases where you can find all the base classes for B

+1
source share

Your code has nothing to do with the method resolution order. Method resolution occurs in the case of multiple inheritance, which is not relevant to your example. Your code is simply incorrect because you think that self.__class__ is actually the same class of where the method is defined, and this is not true:

 >>> class A(object): ... def __init__(self): ... print self.__class__ ... >>> >>> class B(A): ... def __init__(self): ... A.__init__(self) ... >>> B() <class '__main__.B'> <__main__.B object at 0x1bcfed0> >>> A() <class '__main__.A'> <__main__.A object at 0x1bcff90> >>> 

so when you should call:

 super(B, self).__init__(1, b, c) 

you really call:

 # super(self.__class__, self).__init__(1, b, c) super(C, self).__init__(1, b, c) 

EDIT : to better answer the question.

 class A(object): def __init__(self, a): for cls in self.__class__.mro(): if cls is not object: cls._init(self, a) def _init(self, a): print 'A._init' self.a = a class B(A): def _init(self, a): print 'B._init' class C(A): def _init(self, a): print 'C._init' class D(B, C): def _init(self, a): print 'D._init' d = D(3) print da 

prints:

 D._init B._init C._init A._init 3 

(Modified version of the template ).

Now the parent methods are really called implicitly, but I have to agree with python zen where explicit is better than implicit because the code is less readable and the payoff is bad. But be careful that all _init methods have the same parameters, you cannot completely forget about the parents, and I do not suggest doing this.

For single inheritance, the best approach explicitly calls the parent method without calling super . You do not need to name the current class for this, but you should still care about who the parent class is.

Well reads: how-does-pythons-super-do-the-right-thing and the links suggested in this question and especially Python Super are great, but you can't use it

If the hierarchy is likely to change, these are symptoms of poor design and consequences for all parts that use this code and should not be encouraged.

EDIT 2

Another example concerns me, but uses metaclasses. The urwid library uses a metaclass to store the __super attribute in the class, so you just need to access this attribute.

Example:

 >>> class MetaSuper(type): ... """adding .__super""" ... def __init__(cls, name, bases, d): ... super(MetaSuper, cls).__init__(name, bases, d) ... if hasattr(cls, "_%s__super" % name): ... raise AttributeError, "Class has same name as one of its super classes" ... setattr(cls, "_%s__super" % name, super(cls)) ... >>> class A: ... __metaclass__ = MetaSuper ... def __init__(self, a): ... self.a = a ... print 'A.__init__' ... >>> class B(A): ... def __init__(self, a): ... print 'B.__init__' ... self.__super.__init__(a) ... >>> b = B(42) B.__init__ A.__init__ >>> ba 42 >>> 
+1
source share

As far as I know, the following is usually not done. But it seems to work.

Methods in a given class definition always use double-underscore attributes to include the name of the class in which they are defined. That way, if you pinned a class reference in a processed form where instances can see it, you can use this in a super call.

An example containing references to the object itself, by implementing __new__ in the base class:

 def mangle(cls, name): if not name.startswith('__'): raise ValueError('name must start with double underscore') return '_%s%s' % (cls.__name__, name) class ClassStasher(object): def __new__(cls, *args, **kwargs): obj = object.__new__(cls) for c in cls.mro(): setattr(obj, mangle(c, '__class'), c) return obj class A(ClassStasher): def __init__(self): print 'init in A', self.__class super(self.__class, self).__init__() class B(A): def __init__(self): print 'init in B', self.__class super(self.__class, self).__init__() class C(A): def __init__(self): print 'init in C', self.__class super(self.__class, self).__init__() class D(B, C): def __init__(self): print 'init in D', self.__class super(self.__class, self).__init__() d = D() print d 

And, doing a similar thing, but using a meta class and placing __class links on the class objects themselves:

 class ClassStasherType(type): def __init__(cls, name, bases, attributes): setattr(cls, mangle(cls, '__class'), cls) class ClassStasher(object): __metaclass__ = ClassStasherType class A_meta(ClassStasher): def __init__(self): print 'init in A_meta', self.__class super(self.__class, self).__init__() class B_meta(A_meta): def __init__(self): print 'init in B_meta', self.__class super(self.__class, self).__init__() class C_meta(A_meta): def __init__(self): print 'init in C_meta', self.__class super(self.__class, self).__init__() class D_meta(B_meta, C_meta): def __init__(self): print 'init in D_meta', self.__class super(self.__class, self).__init__() d = D_meta() print d 

Doing this all together as one single source file:

 % python /tmp/junk.py init in D <class '__main__.D'> init in B <class '__main__.B'> init in C <class '__main__.C'> init in A <class '__main__.A'> <__main__.D object at 0x1004a4a50> init in D_meta <class '__main__.D_meta'> init in B_meta <class '__main__.B_meta'> init in C_meta <class '__main__.C_meta'> init in A_meta <class '__main__.A_meta'> <__main__.D_meta object at 0x1004a4bd0> 
0
source share

All Articles