Python Inheritance in Depth
Inheritance lets a child class reuse code from a parent class.
super() builds on the parent result
The child greet reuses the parent greet. It adds one character to the result.
class Greeter:
def greet(self):
return "Hello"
class LoudGreeter(Greeter):
def greet(self):
return super().greet() + "!"
print(LoudGreeter().greet())Arguments flow through super()
The child takes one extra argument. It passes the shared one up to the parent setup.
class Person:
def __init__(self, name):
self.name = name
class Student(Person):
def __init__(self, name, course):
super().__init__(name)
self.course = course
student = Student("Cy", "Python")
print(student.name, student.course)The search order in one chain
The __mro__ list shows the search order. Python looks in Manager, then Employee, then object.
class Employee:
pass
class Manager(Employee):
pass
print([cls.__name__ for cls in Manager.__mro__])isinstance vs type()
A Lead is also a Worker by inheritance. The type() check only matches the exact class.
class Worker:
pass
class Lead(Worker):
pass
lead = Lead()
print(isinstance(lead, Worker))
print(type(lead) is Worker)
print(type(lead) is Lead)Composition: a has-a link
A car is not a kind of engine. So Car holds an Engine and passes the work to it.
class Engine:
def start(self):
return "engine on"
class Car:
def __init__(self):
self.engine = Engine()
def start(self):
return self.engine.start()
print(Car().start())object is the root
Every class ends at object. So every value is an instance of object.
class Plain:
pass
print(isinstance(Plain(), object))
print(isinstance(42, object))Example
class Employee:
def __init__(self, name, monthly):
self.name = name
self.monthly = monthly
def pay(self):
return self.monthly
class Manager(Employee):
def __init__(self, name, monthly, bonus):
super().__init__(name, monthly)
self.bonus = bonus
def pay(self):
return super().pay() + self.bonus
ada = Employee("Ada", 3000)
ben = Manager("Ben", 3000, 500)
print(ada.name, ada.pay())
print(ben.name, ben.pay())
print(isinstance(ben, Employee))
print(type(ben) is Employee)
print([cls.__name__ for cls in Manager.__mro__])Key points
- Override: a child method with the same name replaces the parent method
- Use super() to call the parent version of a method
- Method resolution: the order Python searches classes for a method
- Use isinstance(x, Class) to ask if an object belongs to a class or its children
- Composition: a class holds another object and uses it, a has-a link
- object: the root class that every class inherits from in the end
In this example, Employee stores a name and a monthly pay amount. Its pay method returns that amount. Manager takes one extra argument, the bonus. Its __init__ passes name and monthly up with super().__init__. Its pay method calls super().pay() and adds the bonus. Ada is a plain employee and is paid 3000. Ben is a manager and is paid 3500. The isinstance check says Ben counts as an Employee. The type check says his exact class is not Employee. The last line prints the search order for Manager.
Note
isinstance is usually the right check, not type().
A child object passes the parent check, and that is normally what you want.
Try it Yourself
Edit the code, then press Run.
Running is simulated · Python Playground
Exercise
A Manager object is checked with isinstance(ben, Employee). Why is the answer True?