🎓 A1Learner
🔍

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?