Reputation: 1699
I have two superclasses, Father and Mother and they will be inherited by Child.
class Father:
def __init__(self, **kwargs):
self.fathername = kwargs["ffn"] + " " + kwargs["fln"]
self.fatherage = kwargs["fa"]
class Mother:
def __init__(self, **kwargs):
self.mothername = kwargs["mfn"] + " " + kwargs["mln"]
self.motherage = kwargs["ma"]
Here class Child inherits from Father and Mother
class Child(Father, Mother):
def __init__(self, **kwargs):
self.name = kwargs["name"] + " " + kwargs["lastname"]
self.age = kwargs["age"]
I can initialize Father and Mother if I call their __init__()
s separately.
Father.__init__(self, **kwargs)
Mother.__init__(self, **kwargs)
But how do I achieve the same with super()? If I call it like below, it only initializes Father and not Mother (because Father is the next one in the MRO I assume)
super().__init__(**kwargs)
Below is just the __str__()
overridden to show what was assigned.
def __str__(self):
return \
"Im {}, {} years old".format(self.name, self.age) + "\n" + \
"My dad is {} and he is {} years old".format(self.fathername, self.fatherage) + "\n" + \
"My mom is {} and she is {} years old".format(self.mothername, self.motherage)
familyname = "Simpson"
child = Child(**{"name": "Bart", "lastname": familyname, "age": 15, "ffn": "Hommer", "fln": familyname, "fa": 54, "mfn": "Marggie", "mln": familyname, "ma": 46})
When I try to print the object, it will fail because Mother superclass was never initialized (when I used super()
in the Child __init__()
)
print(child)
The program raises a runtime error
Traceback (most recent call last):
File "/tmp/pyadv.py", line 225, in <module>
print(child)
File "/tmp/pyadv.py", line 217, in __str__
return "Im {}, {} years old".format(self.name, self.age) + "\n" + "My dad is {} and he is {} years old".format(self.fathername, self.fatherage) + "\n" + "My mom is {} and she is {} years old".format(self.mothername, self.motherage)
AttributeError: 'Child' object has no attribute 'mothername'
So, how do I use super to initialize two superclasses?
EDIT:
I tried to add super(Father, self).__init__(**kwargs)
and super(Mother, self).__init__(**kwargs)
to the superclasses __init__()
methods but I got the following error:
Traceback (most recent call last):
File "/tmp/pyadv.py", line 225, in <module>
child = Child(**{"name": "Bart", "lastname": familyname, "age": 15, "ffn": "Hommer", "fln": familyname, "fa": 54, "mfn": "Marggie", "mln": familyname, "ma": 46})
File "/tmp/pyadv.py", line 217, in __init__
super().__init__(**kwargs)
File "/tmp/pyadv.py", line 199, in __init__
super(Father, self).__init__(**kwargs)
File "/tmp/pyadv.py", line 208, in __init__
super(Mother, self).__init__(**kwargs)
TypeError: object.__init__() takes no parameters
I also tried to add super(Father, self).__init__()
and super(Mother, self).__init__()
(no arguments inside __init__()
) to the superclasses __init__()
methods but I then got the following error:
Traceback (most recent call last):
File "/tmp/pyadv.py", line 225, in <module>
child = Child(**{"name": "Bart", "lastname": familyname, "age": 15, "ffn": "Hommer", "fln": familyname, "fa": 54, "mfn": "Marggie", "mln": familyname, "ma": 46})
File "/tmp/pyadv.py", line 217, in __init__
super().__init__(**kwargs)
File "/tmp/pyadv.py", line 199, in __init__
super(Father, self).__init__()
File "/tmp/pyadv.py", line 206, in __init__
self.mothername = kwargs["mfn"] + " " + kwargs["mln"]
KeyError: 'mfn'
Solution 1: @blkkngt strip-off below
Solution 2: Root superclass, detailed here.
class Root:
def __init__(self, **kwargs):
pass
class Father(Root):
def __init__(self, **kwargs):
self.fathername = kwargs["ffn"] + " " + kwargs["fln"]
self.fatherage = kwargs["fa"]
super().__init__(**kwargs)
class Mother(Root):
def __init__(self, **kwargs):
self.mothername = kwargs["mfn"] + " " + kwargs["mln"]
self.motherage = kwargs["ma"]
super().__init__(**kwargs)
class Child(Father, Mother):
def __init__(self, **kwargs):
self.name = kwargs["name"] + " " + kwargs["lastname"]
self.age = kwargs["age"]
super().__init__(**kwargs)
def __str__(self):
return \
"Im {}, {} years old".format(self.name, self.age) + "\n" + \
"My dad is {} and he is {} years old".format(self.fathername, self.fatherage) + "\n" + \
"My mom is {} and she is {} years old".format(self.mothername, self.motherage)
familyname = "Simpson"
child = Child(**{"name": "Bart", "lastname": familyname, "age": 15, "ffn": "Homer", "fln": familyname, "fa": 54, "mfn": "Marge", "mln": familyname, "ma": 46})
print(child)
Upvotes: 1
Views: 1007
Reputation: 104762
Multiple inheritance in Python needs to be cooperative. That is, the two parent classes need to be aware of the possibility that each other exist (though they don't need to know any of each other's details). Then whichever parent is named first can call the other parent's __init__
method. That's how super
works, it always calls the next class in the MRO (the method resolution order) of the instance being operated on.
Your code makes it hard to do this correctly, as you're always passing the full kwargs
dict in your super
calls. That becomes a problem when the second parent tries to call the last class in the MRO, object
, which doesn't expect to receive any keyword arguments. Instead, each class's __init__
method should usually explicitly name the parameters it expects, and not pass them on again when it calls super().__init__
(unless it knows that one of its parent classes needs the argument too).
Try this:
class Father:
def __init__(self, ffn, fln, fa, **kwargs): # name the parameters we expect
super().__init__(**kwargs) # pass on any unknown arguments
self.fathername = ffn + " " + fln # use parameters by name, rather than via kwargs
self.fatherage = fa
class Mother:
def __init__(self, mfn, mln, ma, **kwargs):
super().__init__(**kwargs)
self.mothername = mfn + " " + mln
self.motherage = ma
class Child(Father, Mother):
def __init__(self, name, lastname, age, **kwargs):
super().__init__(**kwargs)
self.name = name " " + lastname
self.age = age
def __str__(self):
return \
"Im {}, {} years old".format(self.name, self.age) + "\n" + \
"My dad is {} and he is {} years old".format(self.fathername, self.fatherage) + "\n" + \
"My mom is {} and she is {} years old".format(self.mothername, self.motherage)
familyname = "Simpson"
child = Child(name="Bart", lastname=familyname, age=15, # you can use keyword syntax here
ffn="Homer", fln=familyname, fa=54,
mfn="Marge", mln=familyname, ma=46)
print(child)
Note that in Python 3, you usually don't need to pass any arguments to super()
, it can figure out which class it is being called from and work automatically. In Python 2 you had to specify the current class, but that's not needed any more.
A final note. While I'm sure that your code is only an example, the names of the classes are very poor when it comes to OOP design. Inheritance implies an IS-A relationship between the two classes, which isn't really appropriate for people. The child created in the example code for instance (Bart) is not a Mother
or a Father
, but the code says that he is, since he is an instance of both the Mother
and Father
classes. A better way to describe human relationships with parents would be HAS-A. Each child has a mother and a father. You can make HAS-A relationships using encapsulation. That means, the child object would have a reference to an object for each parent in an attribute. Interestingly, that can be done with just one class (which is probably why you weren't learning this, if you're being taught about inheritance):
class Person:
def __init__(self, firstname, lastname, age, father=None, mother=None):
self.name = firstname + " " + lastname
self.age = age
self.father = father # set the attributes for our parents here
self.mother = mother
fn = "Simpson"
bart = Person("Bart", fn, 15, Person("Homer", fn, 54), Person("Marge", fn, 46))
Upvotes: 2
Reputation:
From the accepted answer in this link:
Invocation via super doesn't call all the parents, it calls the next function in the MRO chain. For this to work properly, you need to use super in all of the
__init__s
class Parent1(object):
def __init__(self):
super(Parent1, self).__init__()
self.var1 = 1
class Parent2(object):
def __init__(self):
super(Parent2, self).__init__()
self.var2 = 2
class Child(Parent1, Parent2):
def __init__(self):
super(Child, self).__init__()
Upvotes: 0