You have the eyes of your mother. One could say, you “inherited” the eyes of your mother. As you may have guessed, this article is about inheritance in Python.
Inheritance is one of the most important features of object orientation. It’s a simple and intuitive concept but even advanced coders circumvent using inheritance because they have this latent feeling of not understanding it 100%. This harms their ability to write clean and effective code, and–ultimately — harms their ability to operate to their full potential. Do you think you could do more to live up to your potential? Good. Then, let’s start learning inheritance in Python.
As a small gift of appreciation for reading this tutorial and improving your programming skills, you’ll get a free PDF download of my Object Orientation Cheat Sheet at the end of this tutorial.
What is Inheritance in Python?
Inheritance allows you to define a class that inherits all methods and properties from another class.
- Parent class, also denoted as base class, is the class you inherit from. In Python, every class can be a parent class.
- Child class, also denoted as derived class, inherits from the Parent class. In Python, you can create a child class that inherits all methods and attributes from the Parent using the
class Child(Parent)
syntax with the parent class enclosed in parentheses.
Here’s the minimal syntax showing how to derive a child class from a parent class using the highlighted parentheses method:
class Parent: pass class Child(Parent): pass
In the following example, you create two classes Parent
and Child
. Parent
has one method p()
that prints the string 'from parent'
when executed. Child
has method c()
that prints the string 'from child'
and the inherited method p()
from the Parent
class.
# Define parent and child classes class Parent: def p(self): return 'from parent' # Child inherits method p() from parent class Child(Parent): def c(self): return 'from child' # Create parent instance and run method parent = Parent() print(parent.p()) # Create child instance and run methods child = Child() print(child.p()) print(child.c())
The output is:
from parent from parent from child
You can see that the child inherits all methods and, not shown, all attributes from the parent class. This can save you a lot of redundancies in practice.
Video Overview
Puzzle and Negative Example
Let’s have a look at a bad example NOT using inheritance. Keep your code understanding skills fresh by solving the puzzle first before you read on.
class Human: def __init__(self, name, ff, iq): self.name = name self.ff = ff # = facebook friends self.iq = iq def befriend(self, other): self.ff += 1 other.ff += 1 def learn(self): self.iq += 1 class Wizard: def __init__(self, name, ff, iq, mana): self.name = name self.ff = ff # = facebook friends self.iq = iq self.mana = mana def befriend(self, other): self.ff += 1 other.ff += 1 def learn(self): self.iq += 1 def magic_friends(self, num): self.ff += num if self.mana>0 else 0 self.mana -= 100 vernon = Human("Vernon", 0, 80) tom = Wizard("Tom", 666, 130, 100) dumbledore = Wizard("Albus", 999, 189, 100) dumbledore.befriend(tom) dumbledore.befriend(vernon) dumbledore.magic_friends(100) print("Friends Vernon: " + str(vernon.ff)) print("Friends Tom: " + str(tom.ff)) print("Friends Dumbledore: " + str(dumbledore.ff))
Before you read on, I have two questions for you:
- What is the output of the above code snippet?
- What’s your idea of making this code more concise?
What is the output of the above code snippet?
Let’s start with the first question. We create two classes Human
and Wizards
. Both have very similar methods and attributes. The only difference is that the Wizard
class has one additional attribute self.mana
and one additional method magic_friends
. Both methods befriend
and magic_friends
modify the instance attribute ff
. With a magic trick, Dumbledore gains 100 additional friends, in addition to Tom and Vernon.
Thus, the result is:
""" Friends Vernon: 1 Friends Tom: 667 Friends Dumbledore: 1101 """
How to Improve This Code?
Regarding the second question, I have already indicated the problem: there are huge redundancies between the two classes Human
and Wizard
. Most methods and attributes are exactly the same. The reason is that, conceptually, a Wizard
is also Human
. And every human needs to have an IQ, a Facebook account, and a name (as you know).
In other words: Every Wizard
is a Human
but not every Human
is a Wizard
.
How can we express this fact in Python object orientation?
The answer is inheritance.
We create a class Human
and a class Wizard
. The class Wizard
is the “child class” of the “parent class” Human
. In this way, the child class Wizard
“inherits” every attribute and method from the parent class Human
. This saves us all the redundant definitions and initializations in the Wizard
class.
See the example:
class Human: def __init__(self, name, ff, iq): self.name = name self.ff = ff # = facebook friends self.iq = iq def befriend(self, other): self.ff += 1 other.ff += 1 def learn(self): self.iq += 1 class Wizard(Human): def __init__(self, name, ff, iq, mana): super().__init__(name, ff, iq) self.mana = mana def magic_friends(self, num): self.ff += num if self.mana>0 else 0 self.mana -= 100 vernon = Human("Vernon", 0, 80) tom = Wizard("Tom", 666, 130, 100) dumbledore = Wizard("Albus", 999, 189, 100) dumbledore.befriend(tom) dumbledore.befriend(vernon) dumbledore.magic_friends(100) print("Friends Vernon: " + str(vernon.ff)) print("Friends Tom: " + str(tom.ff)) print("Friends Dumbledore: " + str(dumbledore.ff)) dumbledore.learn() print("IQ Dumbledore: " + str(dumbledore.iq))
The result is exactly the same as above. As you can see in the last two lines, Dumbledore can still call the method learn()
— even though it is not defined in the Wizard
class. The reason is that the Wizard
class inherits all methods and attributes from the Human
class.
Can you find where we define the inheritance in the code?
One final note: in the constructor of the Wizard
class, we call the constructor of the parent class using "super()"
. This initializes the variables in exactly the same way as the parent constructor from the Human
class.
Calling the Constructor of the Parent Class with super()
Python’s built-in super()
method returns a temporary object of the superclass to help you access its methods. Its purpose is to avoid using the base class name explicitly. It also enables your class to inherit from multiple base classes.
The idea is simple: use super()
to call the methods defined in the parent classes—whether your child class inherits from one or multiple base classes. See the graphic:
Need me to guide you through this example? Watch the explainer video next!
Next, you’ll learn about both cases by example!
Simple Example 1: super() with Single Inheritance
Inheritance in object-oriented programming allows you to create a class hierarchy where one child class inherits all methods from another parent class. This simplifies the development of large software projects and avoids redundant code. You can learn more about the concept of inheritance in our blog tutorial here.
For example, the following code defines the parent class Organism
and the child class Human
. The child class uses super()
to run the constructor method of the parent class.
class Organism: def __init__(self): print('I live') class Human(Organism): def __init__(self): print('I am human') super().__init__() alice = Human()
The output is:
I am human I live
Here you call the base class Organism using the following code:
super().__init__()
A semantically equivalent code call would be:
Organism.__init__(self)
You call the __init__()
method on the base class Organism
and pass a reference to the calling instance as an argument. This way, you could also modify the internal attributes of the self
instance within the base class’ constructor.
However, the advantage of using super().__init__()
compared to ParentClass.__init__(self)
is that you avoid calling the parent class explicitly. This is advantageous because it decouples the child from the parent class. For example, if you changed the name of the ParentClass
to NewParentClass
, the method using super()
would be superior because it would still work while the method using ParentClass.__init__(self)
would throw an error.
Example 2: super() with Multiple Inheritance
One of Python’s unique features compared to other programming languages is that it allows multiple inheritance.
Multiple inheritance means that a class can inherit from multiple parents. For example, a class Human
can inherit from two parent classes: Organism
and Thinker
. Say, you define a method live()
in Organism and think()
in Thinker. If a Human object inherits from both classes, it can call live()
and think()
at the same time! You use the super()
method to call those functions:
class Organism: def live(self): print('I live') class Thinker: def think(self): print('I think') class Human(Organism, Thinker): def __init__(self): print('I am human') super().live() super().think() alice = Human()
The output is:
I am human I live I think
I should mention that in this example, you could also have called self.live()
and self.think()
in the class Human
instead of super().live()
and super().think()
. The output would be the same in both cases. In practice, you’d use the former for instance methods and the latter for class methods. The difference between both is explained in our blog tutorial here.
Python OOP Cheat Sheet
Congratulations, you read through the whole article. Here’s a small reward for your effort: my object-orientation terminology cheat sheet!
Download this cheat sheet as PDF
You can also check out my in-depth Python cheat sheet tutorial with lots of free PDF cheat sheets about various topics in Python such as keywords, data structures, lists, sets, NumPy, pandas, and many more. Simply put your email here and download my free cheat sheets:
Where to Go From Here?
Strategy beats tactics in the long run. You need to have a learning strategy in place to master Python. Do you?
If not, get a quick win by downloading our popular Python cheat sheets (high-res PDF), as well as the long-term solution to your problem (almost daily Python lessons). It’s 100% free. No Spam. Guaranteed.