By Bernd Klein. Last modified: 01 Feb 2022.
On this page ➤
Introduction and Definitions
No object-oriented programming language would be worthy to look at or use, if it didn't support inheritance. Inheritance was invented in 1969 for Simula. Python not only supports inheritance but multiple inheritance as well. Generally speaking, inheritance is the mechanism of deriving new classes from existing ones. By doing this, we get a hierarchy of classes. In most class-based object-oriented languages, an object created through inheritance [a "child object"] acquires all, - though there are exceptions in some programming languages, - of the properties and behaviors of the parent object.
Inheritance allows programmers to create classes that are built upon existing classes, and this enables a class created through inheritance to inherit the attributes and methods of the parent class. This means that inheritance supports code reusability. The methods or generally speaking the software inherited by a subclass is considered to be reused in the subclass. The relationships of objects or classes through inheritance give rise to a directed graph.
The class from which a class inherits is called the parent or superclass. A class which inherits from a superclass is called a subclass, also called heir class or child class. Superclasses are sometimes called ancestors as well. There exists a hierarchical relationship between classes. It's similar to relationships or categorizations that we know from real life. Think about vehicles, for example. Bikes, cars, buses and trucks are vehicles. Pick-ups, vans, sports cars, convertibles and estate cars are all cars and by being cars they are vehicles as well. We could implement a vehicle class in Python, which might have methods like accelerate and brake. Cars, Buses and Trucks and Bikes can be implemented as subclasses which will inherit these methods from vehicle.
Syntax of Inheritance in Python
The syntax for a subclass definition looks like this:
class DerivedClassName[BaseClassName]:
pass
Instead
of the pass
statement, there will be methods and attributes like in all other classes. The name BaseClassName must be defined in a scope containing the derived class definition.
Now we are ready for a simple inheritance example with Python code.
Simple Inheritance Example
We will stick with our beloved robots or better Robot
class from the previous chapters of our Python tutorial to show how the principle of inheritance works. We will
define a class PhysicianRobot
, which inherits from Robot
.
class Robot: def __init__[self, name]: self.name = name def say_hi[self]: print["Hi, I am " + self.name] class PhysicianRobot[Robot]: pass x = Robot["Marvin"] y = PhysicianRobot["James"] print[x, type[x]] print[y, type[y]] y.say_hi[]
OUTPUT:
Hi, I am James
If you look at the code of our PhysicianRobot
class, you can see that we haven't defined any attributes or methods in this class. As the class PhysicianRobot
is a subclass of Robot
, it inherits, in this case, both the method __init__
and
say_hi
. Inheriting these methods means that we can use them as if they were defined in the PhysicianRobot
class. When we create an instance of PhysicianRobot
, the __init__
function will also create a name attribute. We can apply the say_hi
method to the PhysisicianRobot
object y
, as we can see in the output from the code above.
Live Python training
Upcoming online Courses
Enrol here
Difference between type
and isinstance
You should also pay attention to
the following facts, which we pointed out in other sections of our Python tutorial as well. People frequently ask where the difference between checking the type via the type
function or the function isinstance
is lies. The difference can be seen in the following code. We see that isinstance
returns True
if we compare an object either with the class it belongs to or with the superclass. Whereas the equality operator only returns True
, if we compare an object with its own class.
x = Robot["Marvin"] y = PhysicianRobot["James"] print[isinstance[x, Robot], isinstance[y, Robot]] print[isinstance[x, PhysicianRobot]] print[isinstance[y, PhysicianRobot]] print[type[y] == Robot, type[y] == PhysicianRobot]
OUTPUT:
True True False True False True
This is even true for arbitrary ancestors of the class in the inheritance line:
class A: pass class B[A]: pass class C[B]: pass x = C[] print[isinstance[x, A]]
OUTPUT:
Now it should be clear, why PEP 8, the official Style Guide for Python code, says: "Object type comparisons should always use isinstance[] instead of comparing types directly."
Overriding
Let us get back to our new PhysicianRobot
class. Imagine now that an instance of a PhysicianRobot
should say hi in a different way. In this case, we have to redefine the method say_hi
inside of the subclass PhysicianRobot
:
class Robot: def __init__[self, name]: self.name = name def say_hi[self]: print["Hi, I am " + self.name] class PhysicianRobot[Robot]: def say_hi[self]: print["Everything will be okay! "] print[self.name + " takes care of you!"] y = PhysicianRobot["James"] y.say_hi[]
OUTPUT:
Everything will be okay! James takes care of you!
What we have done in the previous example is called overriding. A method of a parent class gets overridden by simply defining a method with the same name in the child class.
If a method is overridden in a class,
the original method can still be accessed, but we have to do it by calling the method directly with the class name, i.e. Robot.say_hi[y]
. We demonstrate this in the following code:
y = PhysicianRobot["Doc James"] y.say_hi[] print["... and now the 'traditional' robot way of saying hi :-]"] Robot.say_hi[y]
OUTPUT:
Everything will be okay! Doc James takes care of you! ... and now the 'traditional' robot way of saying hi :-] Hi, I am Doc James
We have seen that an inherited class can inherit and override methods from the superclass. Besides this a subclass often needs additional methods with additional functionalities, which do not exist in the superclass. An instance of the PhysicianRobot
class will
need for example the method heal
so that the physician can do a proper job. We will also add an attribute health_level
to the Robot
class, which can take a value between 0
and 1
. The robots will 'come to live' with a random value between 0
and 1
. If the health_level
of a Robot
is below 0.8, it will need a doctor. We write a method needs_a_doctor
which returns True
if the value is below 0.8
and False
otherwise. The 'healing' in the heal
method is done by
setting the health_level
to a random value between the old health_level
and 1. This value is calculated by the uniform function of the random module.
import random class Robot: def __init__[self, name]: self.name = name self.health_level = random.random[] def say_hi[self]: print["Hi, I am " + self.name] def needs_a_doctor[self]: if self.health_level