Inheritance reuses a class, polymorphism shares a call
Inheritance lets one class reuse the code of another. Polymorphism means one method call does a different job depending on the object it is called on. Together they let you write one loop that works for many kinds of thing, such as every health check on a server.
By the end of this lesson
Note
I can reuse code through inheritance and use polymorphism.
Key words
| Word | What it means |
|---|---|
| Inheritance | a class taking the attributes and methods of another |
| Superclass | the class that is inherited from, also called the parent |
| Subclass | a class that inherits from another, also called the child |
| Override | to replace an inherited method in a subclass |
| Polymorphism | one call that behaves differently for different objects |
The cells students fill in are left blank in this preview.
A subclass is a kind of its parent
Suppose a monitor runs several checks on a server. A disk check is a kind of check. So is an error count check. They all have a name and they all say ok or FAIL.
Write that shared part once in a parent class, Check. Each child class inherits it and adds only what is different. In Python you write the parent in brackets: class DiskCheck(Check).
The parent class Check
class Check:
def __init__(self, name):
self.name = name
def healthy(self, value):
return True
def report(self, value):
if self.healthy(value):
return self.name + ": ok"
return self.name + ": FAIL"Two subclasses of Check
class DiskCheck(Check):
def __init__(self, name, limit):
super().__init__(name)
self.limit = limit
def healthy(self, value):
return value < self.limit
class ErrorCheck(Check):
def __init__(self, name, most):
super().__init__(name)
self.most = most
def healthy(self, value):
return value <= self.mostA subclass overrides a method to change it
The parent says every check is healthy. That is a placeholder. DiskCheck writes its own healthy, and Python uses that one for disk checks. This is overriding.
super().__init__(name) runs the parent's constructor, so the name is still stored once, in one place. Notice that report is never rewritten. It calls self.healthy, and self decides which version runs.
Make a folder for this lesson
mkdir -p ~/oop2Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
Go into it
cd ~/oop2Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
Write the classes to checks.py
printf 'class Check:\n def __init__(self, name):\n self.name = name\n\n def healthy(self, value):\n return True\n\n def report(self, value):\n if self.healthy(value):\n return self.name + ": ok"\n return self.name + ": FAIL"\n\nclass DiskCheck(Check):\n def __init__(self, name, limit):\n super().__init__(name)\n self.limit = limit\n\n def healthy(self, value):\n return value < self.limit\n\nclass ErrorCheck(Check):\n def __init__(self, name, most):\n super().__init__(name)\n self.most = most\n\n def healthy(self, value):\n return value <= self.most\n' > checks.pyShown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
Run one disk check and one error check
python3 -c "from checks import DiskCheck, ErrorCheck; print(DiskCheck('disk', 90).report(95)); print(ErrorCheck('errors', 5).report(3))"Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
Polymorphism is one call, many behaviours
Look at the loop below. It calls item.report(50) on a list that holds three different kinds of object. The loop never asks which kind. Each object runs its own healthy.
That is polymorphism. You can add a fourth kind of check and the loop does not change.
One loop over three kinds of check
from checks import Check, DiskCheck, ErrorCheck
items = [Check("plain"), DiskCheck("disk", 90), ErrorCheck("errors", 5)]
for item in items:
print(item.report(50))Write the loop to demo2.py
printf 'from checks import Check, DiskCheck, ErrorCheck\n\nitems = [Check("plain"), DiskCheck("disk", 90), ErrorCheck("errors", 5)]\nfor item in items:\n print(item.report(50))\n' > demo2.pyShown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
Run it
python3 demo2.pyShown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
Worked example: the value 50 for each check
| Object | Test it runs | Result for 50 | Printed |
|---|---|---|---|
| Check("plain") | always healthy | True | plain: ok |
| DiskCheck("disk", 90) | 50 < 90 | True | disk: ok |
| ErrorCheck("errors", 5) | 50 <= 5 | False | errors: FAIL |
The cells students fill in are left blank in this preview.
Ask whether an object is a Check
python3 -c "from checks import Check, DiskCheck; d = DiskCheck('disk', 90); print(isinstance(d, Check), isinstance(Check('x'), DiskCheck))"Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
A common mistake
Warning
Many people think inheritance is the way to share code between any two classes. It fits only when the child really is a kind of its parent. A DiskCheck is a Check. Sharing a few lines is a weak reason, and the next lesson shows a better tool.