By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
440,675 Members | 2,255 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 440,675 IT Pros & Developers. It's quick & easy.

Calling __init__ with multiple inheritance

P: n/a
Hello!

Im working with new (object) classes and normaly call init of ther
motherclass with callin super(...), workes fine.

No, I've got a case with multiple inherance and want to ask if this is
the right and common case to call init:

class Mother(object):
def __init__(self, param_mother): print 'Mother'
class Father(object):
def __init__(self, param_father): print 'Father'
class Child(Mother, Father):
def __init__(self, param_mother, param_father):
Mother.__init__(self, param_mother)
Father.__init__(self, param_mother)
child = Child(1, 2)

Thanks, AXEL.
Jul 18 '05 #1
Share this Question
Share on Google+
14 Replies


P: n/a
Axel Straschil wrote:
Im working with new (object) classes and normaly call init of ther
motherclass with callin super(...), workes fine.

No, I've got a case with multiple inherance and want to ask if this is
the right and common case to call init:

class Mother(object):
def __init__(self, param_mother): print 'Mother'
class Father(object):
def __init__(self, param_father): print 'Father'
class Child(Mother, Father):
def __init__(self, param_mother, param_father):
Mother.__init__(self, param_mother)
Father.__init__(self, param_mother)
child = Child(1, 2)


It looks correct -- at least it /looked/ correct before tried to quote it...

Here is an alternative approach that massages the initializer signatures a
bit to work with super() in a multiple-inheritance environment:

class Mother(object):
def __init__(self, p_mother, **more):
print "Mother", p_mother, more
super(Mother, self).__init__(p_mother=p_mother, **more)

class Father(object):
def __init__(self, p_father, **more):
print "Father", p_father, more
super(Father, self).__init__(p_father=p_father, **more)

class Child(Mother, Father):
def __init__(self, p_mother, p_father, **more):
print "Child", p_father, p_mother, more
super(Child, self).__init__(p_mother=p_mother, p_father=p_father,
**more)

Child(1, 2)

Unrecognized parameters are stashed away into the <more> dictionary.

Peter

Jul 18 '05 #2

P: n/a
What if I want to call other methods as well? Modifying your example a
bit, I'd like the reset() method call of Child to invoke both the
Mother and Father reset() methods, without referencing them by name,
i.e., Mother.reset(self).

-------------------
class Mother(object):
def __init__(self, p_mother, **more):
print "Mother", p_mother, more
super(Mother, self).__init__(p_mother=p_mother, **more)
def reset(self):
print 'resetting Mother'

class Father(object):
def __init__(self, p_father, **more):
print "Father", p_father, more
super(Father, self).__init__(p_father=p_father, **more)
def reset(self):
print 'resetting Father'

class Child(Mother, Father):
def __init__(self, p_mother, p_father, **more):
print "Child", p_mother, p_father, more
super(Child, self).__init__(p_mother=p_mother,
p_father=p_father, **more)
def reset(self):
print 'resetting Child'

# I would like to invoke both Mother.reset()
# and Father.reset() here, but it doesn't work.
print 'trying "super"'
super(Child, self).reset()

# These next two do work, but require referencing
# Mother and Father directly.
print "calling directly"
Mother.reset(self)
Father.reset(self)

a = Child(1, 2)
a.reset()
-------------------

Thanks,
Phil

Jul 18 '05 #3

P: n/a
ph*****************@yahoo.com wrote:
What if I want to call other methods as well? Modifying your example a
bit, I'd like the reset() method call of Child to invoke both the
Mother and Father reset() methods, without referencing them by name,
i.e., Mother.reset(self).


[example snipped]

The problem with that aren't incompatible signatures, but the lack of an
implementation of the reset() method at the top of the diamond-shaped
inheritance graph that does _not_ call the superclass method. That method
could be basically a noop:
class Base(object):
def reset(self):
# object does something similar for __init__()
pass

class Mother(Base):
def reset(self):
print "resetting Mother"
super(Mother, self).reset()

class Father(Base):
def reset(self):
print "resetting Father"
super(Father, self).reset()

class Child(Mother, Father):
def reset(self):
print "resetting Child"
super(Child, self).reset()
Child().reset()

It might sometimes be convenient if such methods would magically spring into
existence for the object class, but for now you have to do it manually (or
perhaps someone has come up with a metaclass that I am not aware of).

Peter

PS: See http://www.python.org/2.2/descrintro.html for more on the subject.
Jul 18 '05 #4

P: n/a
Peter Otten wrote:

<snip>
The problem with that aren't incompatible signatures, but the lack of an implementation of the reset() method at the top of the diamond-shaped
inheritance graph that does _not_ call the superclass method. That method could be basically a noop:


<snip>

Ok, now that's cool! Up until now I've been getting along just fine
without new-style classes, but that is about to change. Thanks for the
tip!

Phil

Jul 18 '05 #5

P: n/a
jfj
Peter Otten wrote:

Here is an alternative approach that massages the initializer signatures a
bit to work with super() in a multiple-inheritance environment: super(Father, self).__init__(p_father=p_father, **more)

Is there any advantage using super in this case?
I think the case Father.__init__ (self, params) is simpler
and does the job perfectly well.

super seems to be needed in "Dynamic Inheritance" cases where
we don't know an object's bases and there are comlicated mro issues!
jfj

Jul 18 '05 #6

P: n/a
jfj wrote:
Peter Otten wrote:
Here is an alternative approach that massages the initializer signatures
a bit to work with super() in a multiple-inheritance environment:

super(Father, self).__init__(p_father=p_father, **more)

Is there any advantage using super in this case?
I think the case Father.__init__ (self, params) is simpler
and does the job perfectly well.


I agree.
super seems to be needed in "Dynamic Inheritance" cases where
we don't know an object's bases and there are comlicated mro issues!


Suppose you wanted factor out common code from the Father and Mother classes
into a Parent class -- something neither complicated nor farfetched. With
explicit calls to Parent.__init__() you would end up calling it twice from
Child.__init__(). So when you anticipate that your class hierarchy may
change, or that your classes may be subclassed by users of your library, I
think super() is somewhat less errorprone.

Peter

Jul 18 '05 #7

P: n/a
ph*****************@yahoo.com wrote:
What if I want to call other methods as well? Modifying your example a
bit, I'd like the reset() method call of Child to invoke both the
Mother and Father reset() methods, without referencing them by name,
i.e., Mother.reset(self).

-------------------
class Mother(object):
def __init__(self, p_mother, **more):
print "Mother", p_mother, more
super(Mother, self).__init__(p_mother=p_mother, **more)
def reset(self):
print 'resetting Mother'

class Father(object):
def __init__(self, p_father, **more):
print "Father", p_father, more
super(Father, self).__init__(p_father=p_father, **more)
def reset(self):
print 'resetting Father'

class Child(Mother, Father):
def __init__(self, p_mother, p_father, **more):
print "Child", p_mother, p_father, more
super(Child, self).__init__(p_mother=p_mother,
p_father=p_father, **more)
def reset(self):
print 'resetting Child'

# I would like to invoke both Mother.reset()
# and Father.reset() here, but it doesn't work.
print 'trying "super"'
super(Child, self).reset()

# These next two do work, but require referencing
# Mother and Father directly.
print "calling directly"
Mother.reset(self)
Father.reset(self)

a = Child(1, 2)
a.reset()
-------------------

Thanks,
Phil

if you're going to be doing a bunch of these, you could define your own variant
of the super type that calls all the methods in the mro with the given name.
Below is a minor modification to the pure-python version of built-in super that
illustrates this. There is no error-checking (e.g., if an attribute is callable
in one class, and not callable in another you will hit problems) and it's not
tested beyond what you see:

"""Hack on Super defined in http://python.org/2.2/descrintro.html
to call all super-class methods"""

class PolySuper(object):
def __init__(self, type, obj=None):
self.__type__ = type
self.__obj__ = obj
def __get__(self, obj, type=None):
if self.__obj__ is None and obj is not None:
return Super(self.__type__, obj)
else:
return self
def __getattr__(self, attr):
if isinstance(self.__obj__, self.__type__):
starttype = self.__obj__.__class__
else:
starttype = self.__obj__
mro = iter(starttype.__mro__)
for cls in mro:
if cls is self.__type__:
break
# Note: mro is an iterator, so the second loop
# picks up where the first one left off!
for cls in mro:
if attr in cls.__dict__:
x = cls.__dict__[attr]
if hasattr(x, "__get__"):
x = x.__get__(self.__obj__)

# return x
# We want all the resolved methods, in order
yield x
#raise AttributeError, attr

# Add this crude callable interface
def __call__(self, attr, *args, **kw):
methods = list(self.__getattr__(attr))
return [method(*args, **kw) for method in methods]
Then, your classes get defined like so:

class Mother(object):
def __init__(self, p_mother, **more):
print "Mother", p_mother, more
def reset(self):
print 'resetting Mother'

class Father(object):
def __init__(self, p_father, **more):
print "Father", p_father, more
def reset(self):
print 'resetting Father'

class Child(Mother, Father):
def __init__(self, p_mother, p_father, **more):
print "Child", p_mother, p_father, more
PolySuper(Child, self)("__init__", p_mother = p_mother, p_father =
p_father)
def reset(self):
print 'resetting Child'
PolySuper(Child, self)("reset")
c = Child("M1","F1") Child M1 F1 {}
Mother M1 {'p_father': 'F1'}
Father F1 {'p_mother': 'M1'} c.reset() resetting Child
resetting Mother
resetting Father


HTH
Michael

Jul 18 '05 #8

P: n/a
jfj
Peter Otten wrote:
jfj wrote:
Peter Otten wrote:
Here is an alternative approach that massages the initializer signatures
a bit to work with super() in a multiple-inheritance environment:

super(Father, self).__init__(p_father=p_father, **more)


Is there any advantage using super in this case?
I think the case Father.__init__ (self, params) is simpler
and does the job perfectly well.


I agree.


Ok. thanks for the confirmation.
super seems to be needed in "Dynamic Inheritance" cases where
we don't know an object's bases and there are comlicated mro issues!

Suppose you wanted factor out common code from the Father and Mother classes
into a Parent class -- something neither complicated nor farfetched. With
explicit calls to Parent.__init__() you would end up calling it twice from
Child.__init__(). So when you anticipate that your class hierarchy may
change, or that your classes may be subclassed by users of your library, I
think super() is somewhat less errorprone.


I accept the case that you avoid bugs if you extend the hierarchy
upwards. Although that's rare.

As for the case where the users of the library want to subclass, I don't
see a problem. They know they must subclass from class XXX and so they
call XXX.__init__ to construct it.

In the case of Parent diamond inheritance, super() can avoid calling
the __init__ of parent twice? How?
jfj

Jul 18 '05 #9

P: n/a
jfj wrote:
As for the case where the users of the library want to subclass, I don't
see a problem.**They*know*they*must*subclass*from*class* XXX*and*so*they
call XXX.__init__ to construct it.
I was thinking of

class Child(Father, Mother):
pass

where Father and Mother have a common base class Parent. That would be
initialized twice:
class Parent(object): .... def __init__(self):
.... print "parent"
.... class Father(Parent): .... def __init__(self):
.... print "father"
.... Parent.__init__(self)
.... class Mother(Parent): .... def __init__(self):
.... print "mother"
.... Parent.__init__(self)
.... class Child(Father, Mother): .... def __init__(self):
.... print "child"
.... Father.__init__(self)
.... Mother.__init__(self)
.... Father() father
parent
<__main__.Father object at 0x402ad66c> Mother() mother
parent
<__main__.Mother object at 0x402ad38c> Child() child
father
parent
mother
parent # <-- the culprit: parent initialized a second time
<__main__.Child object at 0x402ad66c>

You have several options now:
- Write __init__() in such a way that calling it twice does no harm
- Ensure that it is only called once by adding a self.initialized flag
- Document that a user class may not inherit from both Father and Mother
but the best is IMO
- let super() do the work
In the case of Parent diamond inheritance, super() can avoid calling
the __init__ of parent twice?**How?


That's the best part -- it's automatic:
class Parent(object): .... def __init__(self):
.... print "parent"
.... super(Parent, self).__init__()
.... class Father(Parent): .... def __init__(self):
.... print "father"
.... super(Father, self).__init__()
.... class Mother(Parent): .... def __init__(self):
.... print "mother"
.... super(Mother, self).__init__()
.... class Child(Father, Mother): .... def __init__(self):
.... print "child"
.... super(Child, self).__init__()
.... Father() father
parent
<__main__.Father object at 0x402ad38c> Mother() mother
parent
<__main__.Mother object at 0x402ad3cc> Child()

child
father
mother
parent # <-- parent only once
<__main__.Child object at 0x402ad38c>

That was a lot of dull code -- but you asked :-)
Seriously, I think super() scales better in a scenario with multiple
inheritance, though I won't start a holy war about the issue.

Peter
Jul 18 '05 #10

P: n/a
Hello!

Thanks to all for the very interesting postings!

I came to the following:

For single inheritance, super is a nice tool if you will recfactoring
the class later.

For multiple inheritance, if you want to use super, you have to have
very much knowledge of the classes you inheritance. For me, OOP is to
not have to have the deep inner knowledge of the classes I inheritance
from.

Also, for multiple inheritance, if think
Mother1.__init__(self, ...)
Mother2.__init__(self, ...)
Mother3.__init__(self, ...)
would be more clear to read then
super(MyClass).__init__(Maby this will do some magic thing)

Also, I realy dislike
__init__(self, param, **eat_some_needless_stuff)

If I later extend my class and also add some parameters to __init__,
what will happen to the classes using the baseclass?

Also, lool at that:
class Mother(object):
def __init__(self, param_mother='optional', **eat):
print 'Mother'
class Father(object):
def __init__(self, param_father='optional', **eat):
print 'Father'
class Child(Mother, Father):
def __init__(self, **ham):
super(Child, self).__init__(**ham)
child = Child(param_mother=1, param_father=1)

Father's init will not be called.

Thanks,
AXEL.
--
"Aber naja, ich bin eher der Forentyp." Wolfibolfi's outing in
http://www.informatik-forum.at/showp...2&postcount=10
Jul 18 '05 #11

P: n/a
Axel Straschil wrote:
I came to the following:

For single inheritance, super is a nice tool if you will recfactoring
the class later.

For multiple inheritance, if you want to use super, you have to have
very much knowledge of the classes you inheritance.


And for multiple inheritance, if you don't use super, you're likely to
call the same method twice if you ever have a diamond inheritance problem.

STeVe
Jul 18 '05 #12

P: n/a
Axel Straschil wrote:
Thanks to all for the very interesting postings!
You're welcome.
I came to the following:

For single inheritance, super is a nice tool if you will recfactoring
the class later.
Or if you start out with a diamond inheritance graph from the beginning.
For multiple inheritance, if you want to use super, you have to have
very much knowledge of the classes you inheritance. For me, OOP is to
not have to have the deep inner knowledge of the classes I inheritance
from.
I failed to bring my point that you need _less_ knowledge across then.
Also, for multiple inheritance, if think
Mother1.__init__(self, ...)
Mother2.__init__(self, ...)
Mother3.__init__(self, ...)
would be more clear to read then
super(MyClass).__init__(Maby this will do some magic thing)
For an advantage to show up you need at least three levels of inheritance,
see my example with the Parent class.
Also, I realy dislike
__init__(self, param, **eat_some_needless_stuff)
In turn, I really like the name you gave the dictionary parameter :-)
Normalize the initializer signature to a common set of arguments. Or pass a
single parameter and take the actual information from its attributes. Or
write a custom dispatcher, that passes only parameters that correspond to
formal arguments...
If I later extend my class and also add some parameters to __init__,
what will happen to the classes using the baseclass?

Also, lool at that:
class Mother(object):
def __init__(self, param_mother='optional', **eat):
print 'Mother'
class Father(object):
def __init__(self, param_father='optional', **eat):
print 'Father'
class Child(Mother, Father):
def __init__(self, **ham):
super(Child, self).__init__(**ham)
child = Child(param_mother=1, param_father=1)

Father's init will not be called.


Change Father/Mother.__init__() to call the superclass initializer. It may
be counterintuitive, but it works.

Peter

Jul 18 '05 #13

P: n/a
jfj
Peter Otten wrote:
Child()


child
father
mother
parent # <-- parent only once
<__main__.Child object at 0x402ad38c>


D-uh?

################################################
class Parent(object):
def __init__(self):
print "parent"
super(Parent, self).__init__()

class Father(Parent):
def __init__(self):
print "father"
super(Father, self).__init__()
print "D-uh"

class Mother(Parent):
def __init__(self):
print "mother"
super(Mother, self).__init__()
print "D-oh"

class Child(Father, Mother):
def __init__(self):
print "child"
super(Child, self).__init__()

Child()
################################################

This prints
child
father
mother
parent
D-oh
D-uh

Therefore super is a very intelligent function indeed!
jf
Jul 18 '05 #14

P: n/a
Hello!
Also, lool at that:
class Mother(object):
def __init__(self, param_mother='optional', **eat):
print 'Mother'
class Father(object):
def __init__(self, param_father='optional', **eat):
print 'Father'
class Child(Mother, Father):
def __init__(self, **ham):
super(Child, self).__init__(**ham)
child = Child(param_mother=1, param_father=1)
Father's init will not be called.

Change Father/Mother.__init__() to call the superclass initializer. It may
be counterintuitive, but it works.


OK, thanks, with the super(...).__init__() in Father/Mother it workes
and makes sense.

So, the last thing a *realy* don't like ist the
__init__(self, param, **ignore_the_rest) thing.

Anyone had troubles with that, or should I cust take this as a "python
way of thinking" ... ;-), and getting used to that?

Thanks,
AXEL.
--
"Aber naja, ich bin eher der Forentyp." Wolfibolfi's outing in
http://www.informatik-forum.at/showp...2&postcount=10
Jul 18 '05 #15

This discussion thread is closed

Replies have been disabled for this discussion.