"You Have Your Mother's Eyes" • Inheritance in Python Classes (Harry Potter OOP Series #5)
Year 5 at Hogwarts School of Codecraft and Algorithmancy • Inheritance
The Ancient Library shimmered under a sea of astral lanterns, their cool silver light bathing the earnest faces of fifth-year students. In their hands, a book thrummed with the aura of arcane secrets and ancestral wisdom:
Ethereal Heirlooms: Unraveling Inheritance and Harnessing Hierarchies
Last year's expedition had led them through the intricate labyrinth of class interactions, turning these budding wizards into adept Python architects. The hushed whispers of their former lessons lingered in the air, dancing with the dust motes amongst rows of timeworn tomes, serving as a poignant reminder of the path they had trodden.
The forthcoming chapter of their quest held the keys to the grand kingdom of inheritance, a kingdom of castles built upon the foundations of those that came before. Each snippet of code, each method was a stepping stone leading to a more complex citadel of interconnected classes.
Fifth-years, as the Ancient Library's celestial glow dances on the parchment before you, ready yourselves for another deep dive into Python's enchanting world. A chronicle of further learning, unveiling, and mastery is on the horizon.
Students learn about inheritance in the fifth year at Hogwarts School of Codecraft and Algorithmancy.
[Spoiler Alert: There are spoilers from the final Harry Potter book in the next paragraph]
"You have your mother's eyes" is a recurring phrase in the Harry Potter series that seems appropriate as the title for this article about inheritance. Many wizards who knew Harry's mother said that phrase to Harry at some stage. These words were famously also Snape's last words to Harry. Except that…they weren't. Not in the books, in any case. Snape's last words in the book were "Look...at...me". But the reference may have been too subtle for the films. So, in the film, this changed to "Look at me. You have your mother's eyes".
The Curriculum at Hogwarts (Series Overview)
This is the fifth in a series of seven articles, each linked to a year at Hogwarts School of Codecraft and Algorithmancy:
Year 5 (this article): Inheritance. The students are ready for this now they're older
Year 6: Students learn about special methods. You may know these as dunder methods
Most posts on The Python Coding Stack are available for free and in full. If you like the content and find it useful, you can support the publication through a paid subscription.
Professors Are Wizards. So Are The Students
Over the past few years at Hogwarts School of Codecraft and Algorithmancy, you built a set of classes to deal with the wizarding world. The classes represent the "things" that describe the situation. So far, you have the following classes:
Each of these classes has its own attributes—data attributes and methods. The data attributes contain the information needed by the objects. The methods give the objects the functionality to do things with the data.
Here's the code with all the classes you have so far:
There are professors and students at Hogwarts School of Codecraft and Algorithmancy. There's different information that's relevant to the two categories. Teachers teach certain subjects and mark exams, for example. Students study several subjects and take exams.
Therefore, we need different classes to represent professors and students. However, professors and students are also wizards. The new classes to represent professors and students should also have the attributes from the
Wizard class you defined in previous years.
It would be wasteful to define two separate classes for professors and students with lots of code in common. That goes against the DRY principle—Don't Repeat Yourself.
Instead, you can create two new classes,
Student, that inherit from
All professors are wizards. But not all wizards are professors.
All students are wizards. But not all wizards are students.
When you create a class that inherits from another one, the child class starts off having the same attributes as the parent class. Then, you can add attributes or even change some of the existing ones, as you'll see in this year's curriculum.
Note: several terms can be used to refer to classes when dealing with inheritance. When a class inherits from another class, it can be called a subclass, a derived class or a child class. The class it inherits from can be called a superclass, a base class or a parent class. I'll mostly use the terms 'parent class' and 'child class' as I think they're the most intuitive at this stage.
Professor That Inherits From
Let's create a
Professor class. I'm also showing the
Wizard class in full in the following code segment:
Let's look at the differences between defining the
The class name
Professoris followed by parentheses containing the name of another class. This shows that
Wizard. It has access to the same data attributes and methods.
Professorclass has its own
__init__()method. It includes the parameters
birth_year. But there's also
subject, which is an additional parameter.
There's a "strange" line of code in the
__init__()method that starts with
super(). We'll go through what this line does shortly.
Professorhas an additional data attribute,
.subject. You assign the value from the argument
subjectto this data attribute.
Let's first look at this short example to understand inheritance at a top level:
ParentClass object is an instance of
ParentClass but not of
ChildClass. However, since
ChildClass inherits from
ChildClass object is an instance of both classes.
Let's return to the
Professor class in the main code and focus on the following line of code:
super().__init__(name, patronus, birth_year)
This calls the superclass's
__init__() method. In this case,
Professor inherits from
Wizard. Therefore, this line calls
Wizard.__init__(). This call includes the parameters
birth_year, but not
subject since that's not a parameter in the
An instance of
Professor is also an instance of
Wizard. Therefore, when you initialise a
Professor instance using its
__init__() method, you also initialise it as a
Wizard instance first.
We can have more complex inheritance setups, and
super() will also handle these cases. However, in this series, we won't explore these cases.
Adding and overriding methods in a subclass
A child class starts off with the same attributes as the parent class. You've already seen how to add new data attributes, such as
.subject. You can also add methods to the child class that are not present in the parent class:
Professor class now has a method called
assess_student() that's unique to this child class and doesn't exist in the parent class. You'll finish writing this method shortly.
You can add new methods to a child class, as you did with
assess_student(). But you can also change methods that exist in the parent class:
Wizard class has its own
assign_wand() method. However, you define another
assign_wand() as part of the
Professor subclass. This new method overrides the one in the parent class. So, when you call this method on a
Professor object, the method defined in
Professor is called.
In this case, the first line in
You've already seen
super() used. This line calls the
assign_wand() method of the superclass, which is
Wizard. Therefore the method in the child class,
Professor, will first do whatever the method in the parent class does, but it will also perform additional tasks. In this case, it increases the professor's skill.
If you want the new method to be significantly different from the one in the parent class, you don't need to use
super() to call the parent's method. Whether you want to build on the parent's method depends on what you need your method to do.
Student That Inherits from
Next, you can create the
Student class. Since a student at Hogwarts School of Codecraft and Algorithmancy is also a wizard, this new class also inherits from
You know what to expect. Since
Student inherits from
Wizard, you'll see some similarities with how you dealt with the
Professor class earlier:
You define the class using the line
class Student(Wizard). When you add
Wizardwithin parentheses after the
classkeyword and the new class's name, you show that
The special method has five parameters, including
self. There's an additional parameter named
school_yearcompared to the parent class's
Student.__init__()to initialise the object as a
Wizardas well as a
The final lines in
__init__()create two new data attributes to store the school year and the student's grades. The
.subject_gradesdata attribute is a dictionary, so it can store the subject name as a key and the test scores as values.
You define three new methods unique to the
Studentclass. These methods:
Assign a house to the student
Allow the student to take an exam
Assign subjects that the student is taking at school
The first of the three methods is
assign_house_using_sorting_hat(). We'll get back to this method in the final year.
The second method is
take_exam(), which takes the subject name and the grade as arguments. This method updates the
.subject_grades data attribute, which is a dictionary.
The third method,
assign_subjects(), accepts an iterable with the names of the subjects the student is learning and creates the dictionary with all the subjects. Initially,
None is the value associated with all subject names.
Caveat: These methods are not perfect. We could discuss ways of improving them to make them more robust. However, the aim of this series of articles is not to create the perfect software to solve Hogwarts' administrative problems. Instead, we want to ensure we understand how classes work.
It's time to return to the
Professor class and write the code for the
Since you have objects of type
Student, you can pass these objects to
Professor.assess_student(). This is the value associated with the
student parameter in
You call this method when a professor needs to assess a student. The method first checks whether the subject the professor teaches,
self.subject, is one of the subjects the student is learning—these subjects are stored in the
If the student is learning the subject, the method calls
student.take_exam(). You pass the subject taught by the professor,
self.subject, and the grade. Recall that
self refers to the
Professor instance in this case since you're using it within the
Professor class. You'll look at this method call in more detail later in this article.
Let's Try These New Classes
It's time to use these new subclasses to see whether they work as intended. You can return to the
making_magic.py script you used in previous years to test these classes:
Let's look at all the steps included in this script:
You create two
hermione. Note that in previous versions of this script from previous years, these objects were instances of
Wizard. Now that you have a
Studentsubclass, you use this new class.
You create two
You create two
mcgonagall. You call
assign_house()for both instances. Note that the
Professorclass doesn't have a distinct
assign_house()defined for the class. However, it inherits the method from
Wizard, which is the parent class. The text displayed when you assign a wand to a wizard still has some issues. You'll deal with these in Year 6.
Next, you assign two subjects to
harry. You don't assign any subjects to
snape.assess_student()twice, once for each student. Since Harry Potter studies Potions, the method assigns a grade to the instance
harry. However, the instance
"Potions"as one of the subjects.
I'm using strings to represent subject names in this example. I don't want the code in this series to get out of hand. However, you may want to consider creating a
Subject class to deal with subjects studied at Hogwarts School of Codecraft and Algorithmancy.
Following The Trail (But You Don't Need To)
Let's look at the line in which Snape assesses Harry and gives him a low mark!
Note that this line doesn't have any reference to the subject. This is because, at Hogwarts School of Codecraft and Algorithmancy, each teacher only teaches one subject (the same as the Hogwarts in the original Harry Potter stories.)
However, this line changes Harry Potter's Potions grade:
The value for
harry.subject_grades changes from
20 after the call to
Let's follow the trail:
This refers to the
We access one of the
Professorobject's attributes: the method
You pass the
harryand the integer
20to this method.
You'll recall from earlier years at Hogwarts that this call,
snape.assess_student(harry, 20), is equivalent to:
Professor.assess_student(snape, harry, 20)
Professor instance is passed to the
self parameter, which is the first parameter in all instance methods. Therefore, the method has access to the objects
harry. And the integer
20, of course!
This means the method also has access to all the attributes that belong to
harry, including the subject taught by the professor.
Let's zoom in on the following line of code in the
In this example, this is equivalent to the following:
Our journey now takes us to the
Student.take_exam() method. The call above is equivalent to:
Student.take_exam(harry, "Potions", 20)
take_exam() method has the following line of code:
self.subject_grades[subject] = grade
This is equivalent to:
harry.subject_grades["Potions"] = 20
This updates the value associated with the key
"Potions" in the dictionary.
Object-oriented programming allows you to abstract all this functionality within the objects. So, when you call
snape.assess_student(harry, 20), you don't need to worry about what happens behind the scenes. Whatever is needed for Snape to assess Harry and assign him a grade of 20% will happen out of sight.
Subclass or Derived Class or Child Class: A class that inherits from another one
Superclass or Base Class or Parent Class: A class from which other classes inherit
"You have your mother's eyes". Harry Potter clearly inherited his mother's eyes. We all inherit characteristics from our parents, but we also have our own characteristics. We're distinct from our parents. This is similar to inheritance in classes. Child classes inherit attributes from their parent classes, but they can change those attributes and have new ones.
This brings us to the end of Year 5. Enjoy the holidays. Year 6 is special. Literally. You'll learn about special methods (aka dunder methods).
Next article in this series: Year 6 • Time for Something Special • Special Methods in Python Classes
Code in this article uses Python 3.11
Most posts on The Python Coding Stack are available for free and in full. If you like the content and find it useful, you can support the publication through a paid subscription
Recently published articles on The Stack:
Casting A Spell • More Interaction Between Classes. Year 4 at Hogwarts School of Codecraft and Algorithmancy • More on Methods and Classes
An Object That Contains Objects • Python's Containers. Containers • Part 4 of the Data Structure Categories Series
Zen and The Art of Python
turtleAnimations • A Step-by-Step Guide. Python's
turtleis not just for drawing simple shapes
The One About The Taxi Driver, Mappings, and Sequences • A Short Trip to 42 Python Street. How can London cabbies help us learn about mappings and sequences in Python?
Deconstructing Ideas And Constructing Code • Using the Store-Repeat-Decide-Reuse Concept. Starting to code on a blank page • How do you convert your ideas into code?
I've added a new page on The Python Coding Stack's homepage called The Python Series—that's ‘series’ as a plural noun! It's a shame ‘series’ is a word that has the same singular and plural form. For the avid readers, you know that I publish several stand-alone articles, but I also publish articles that are part of series (pl.), such as this article you just finished reading. I'll use this page to keep all series in one place. At the moment, there are links to The Data Structure Category Series and The Harry Potter Object-Oriented Programming Series. I'll add more links to future series here, too.
The Python Coding Stack is now two months old. This is the 14th article I'm publishing and I'm pleased that so many of you are finding these articles useful. There are over 500 of you subscribed to this Substack now, and I know many more read these articles, too. I enjoy writing. And I'm glad that there are people who enjoy reading what I write, too. Thank you.
Most articles will be published in full on the free subscription. However, a lot of effort and time goes into crafting and preparing these articles. If you enjoy the content and find it useful, and if you're in a position to do so, you can become a paid subscriber. In addition to supporting this work, you'll get access to the full archive of articles and some paid-only articles.