×
By the end of this chapter, you should be able to:
super
keyword doessuper
In many languages that support object-oriented programming, you can define a class which inherits from another class. Python takes things even further; in Python, we can do what is called multiple inheritance. This means that one class can inherit from many other classes!
Here's an example of single inheritance:
class Vehicle: def __init__(self, make, model, year): self.make = make self.model = model self.year = year def honk(self): return "Beep!" class Car(Vehicle): def __init__(self, make, model, year): super().__init__(make, model, year) # this is python3 specific! self.wheels = 4
Notice that when we define the Car
class, we pass in the Vehicle
class. Then, inside of the car's __init__
method we make a call to super().__init__
, which refers to the parent class's __init__
method. Calling super
in this way ensures that car instances are assigned a make
, model
, and year
, but saves us from having to repeat ourselves in the logic for the Car
's __init__
method.
Inheritance also saves us from having to duplicate instance methods: instances of the child class automatically gain access to instance methods in the parent class. Take a look:
mack_truck = Vehicle("Mack", "Titan", 2015) car = Car("Honda", "Civic", 2004) mack_truck.honk() # "Beep!" car.honk() # "Beep!"
In this case, we don't need to define a honk
method for cars. Since the Car
class inherits from Vehicle
, any car instances will automatically have access to the honk
method from the Vehicle
class.
The car and vehicle example is fine when we want a class to inherit from one other class. But what if we want it to inherit from multiple classes? In this case, Python lets us do multiple inheritance by passing in multiple classes when we create a new class! Here's an example:
class Aquatic: def __init__(self, name): self.name = name def swim(self): return f"{self.name} is swimming" def greet(self): return f"I am {self.name} of the sea!" class Ambulatory: def __init__(self, name): self.name = name def walk(self): return f"{self.name} is walking" def greet(self): return f"I am {self.name} of the land!" class Penguin(Aquatic, Ambulatory): def __init__(self, name): super().__init__(name=name) jaws = Aquatic("Jaws") lassie = Ambulatory("Lassie") captain_cook = Penguin("Captain Cook")
Here we define two classes: Aquatic
(for things that can swim) and Ambulatory
(for things that can walk). Instances of the Aquatic
class have a name
, a swim
method, and a greet
method. Similarly, instances of the Ambulatory
class have a name
, a walk
method, and a greet
method.
We then define a third class called Penguin
, which inherits from both Aquatic
and Ambulatory
. Finally, we create an instance from each of these classes.
Let's examine what happens when we try to call methods on each of these instances. You should see the following:
jaws.swim() # 'Jaws is swimming' jaws.walk() # AttributeError: 'Aquatic' object has no attribute 'walk' jaws.greet() # 'I am Jaws of the sea!' lassie.swim() # AttributeError: 'Ambulatory' object has no attribute 'swim' lassie.walk() # 'Lassie is walking' lassie.greet() # 'I am Lassie of the land!' captain_cook.swim() # 'Captain Cook is swimming' captain_cook.walk() # 'Captain Cook is walking' captain_cook.greet() # 'I am Captain Cook of the sea!'
Notice that because Penguin
inherits from both Aquatic
and Ambulatory
, instances of the Penguin
class have access to both the swim
method and the walk
method.
What happens with the greet
method, though? In this case, there's a conflict, since both Aquatic
and Ambulatory
have their own greet
methods! In this case, it looks like Penguin
inherits the greet
method from Aquatic
and ignores the greet
method from Ambulatory
. (Notice that when we defined Penguin
, we passed in Aquatic
first, and then Ambulatory
; what happens if you switch the order?)
So how does Python know where to search for methods? Whenever you create a class, Python sets a Method Resolution Order, or MRO, for that class. This order determines the order in which Python will look for methods on instances of that class. With single inheritance (or even simple examples of multiple inheritance) the order isn't so hard to deduce, but for really complex multiple inheritance things can get tricky. If you ever need to see what the MRO is for a class, you can take a look at the __mro__
attribute for that class, use the mro()
method, or, even better, get some help
:
Penguin.__mro__ # (<class 'multiple.Penguin'>, <class 'multiple.Aquatic'>, <class 'multiple.Ambulatory'>, <class 'object'>) # OR Penguin.mro() # returns a list to us # EVEN BETTER! help(Penguin) # gives us a detailed chain
When you're ready, move on to Special Methods and Polymorphism