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 out __init__
calls appear to the be the commonly accepted "smart" way to do super class initialization. However in the event that the class hierarchy is likely to change, I have been using the uncommented form, until recently.
It appears that in the call to the super constructor for B
in the above hierarchy, that B.__init__
is called again, self.__class__
is actually C
, not B
as I had always assumed.
Is there some way in Python-2.x that I can maintain proper MRO (with respect to initializing all parent classes in the correct order) when calling super constructors while not naming the current class (the B
in in super(B, self).__init__(1, b, c)
)?
Your code has nothing to do with method resolution order. Method resolution comes in the case of multiple inheritance which is not the case of your example. Your code is simply wrong because you assume that self.__class__
is actually the same class of the one where the method is defined and this is wrong:
>>> 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 are indeed calling:
# super(self.__class__, self).__init__(1, b, c)
super(C, self).__init__(1, b, c)
EDIT: trying 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 d.a
prints:
D._init
B._init
C._init
A._init
3
(A modified version of template pattern).
Now parents' methods are really called implicitly, but i have to agree with python zen where explicit is better than implicit because the code is lesser readable and the gain is poor. But beware that all _init
methods have the same parameters, you cannot completely forget about parents and I don't suggest to do so.
For single inheritance, a better approach is explicitly calling parent's method, without invoking super
. Doing so you don't have to name the current class, but still you must care about who is the parent's class.
Good reads are: how-does-pythons-super-do-the-right-thing and the links suggested in that question and in particularity Python's Super is nifty, but you can't use it
If hierarchy is likely to change is symptoms of bad design and has consequences in all the parts who are using that code and should not be encouraged.
EDIT 2
Another example comes me in mind, but which uses metaclasses. Urwid library uses metaclass to store an attribute, __super
, in class so that you need just to access to that attribute.
Ex:
>>> 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__
>>> b.a
42
>>>
Short answer: no, there's no way to implicitly invoke the right __init__
with the right arguments of the right parent class in Python 2.x.
Incidentally, 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 can stop working if you introduce a new subclass that uses multiple inheritance.
See http://fuhm.net/super-harmful/ for a longer description of the issue (with pictures).
Perhaps what you are looking for is 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()
Prints "I am class A"
What the metaclass does is overriding the initial creation of the B class (not the object) and makes sure that the builtin dictionary for each B object now contains a bases array where you can find all the baseclasses for B
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With