Inheritance

Intermediate

Introduction

Inheritance lets a new class start from an existing one. The new class (the child) gets every attribute and method of the old one (the parent), and can add new ones or replace some.

When several kinds of things share most of their behaviour, copying code between them means fixing every bug several times. Inheritance puts the shared part in one place and lets each kind describe only what makes it different.

How it works

Calling the parent with super()

class Person:
    def __init__(self, name):
        self.name = name

class Student(Person):
    def __init__(self, name, student_id):
        super().__init__(name)
        self.student_id = student_id

s = Student("Nadia", "CSE-101")
print(s.name, s.student_id)
print(isinstance(s, Person))
Open in compiler

When a child needs its own __init__, it should still let the parent do its part. super().__init__(name) calls Person's __init__ on the same object. At that moment the call stack holds three frames: the global frame and two __init__ frames, Student's with Person's on top, both with self pointing to the same object. Then Student adds its own attribute, student_id.

isinstance(s, Person) is True: a Student is also a Person.

A parent method that calls the child's

class Shape:
    def area(self):
        return 0

    def describe(self):
        return f"{type(self).__name__} with area {self.area()}"

class Square(Shape):
    def __init__(self, side):
        self.side = side

    def area(self):
        return self.side * self.side

print(Square(4).describe())
Open in compiler

describe is written once, in Shape, but it calls self.area(). For a Square, self is a Square, so Square's area runs and the calculation box shows self.side * self.side → 4 * 4 → 16. The parent defines the plan; each child fills in the details.

Syntax

class Animal:
    def speak(self):
        return "..."

class Dog(Animal):
    def speak(self):
        return "Woof"

Example

class Animal:
    def __init__(self, name):
        self.name = name

    def speak(self):
        return "..."

class Dog(Animal):
    def speak(self):
        return "Woof"

class Cat(Animal):
    def speak(self):
        return "Meow"

for pet in [Dog("Tommy"), Cat("Mini")]:
    print(pet.name, "says", pet.speak())
Open in compiler

class Dog(Animal): means a Dog is an Animal. Dog has no __init__ of its own, so step into Dog("Tommy") and you will see Animal's __init__ run and set self.name.

Dog and Cat each override speak: they define their own version, which Python uses instead of Animal's. The loop calls pet.speak() without knowing which kind of animal it has, and each object answers in its own way. This is called polymorphism.

Line by line

  1. class Animal: has __init__ and a general speak.
  2. class Dog(Animal): and class Cat(Animal): each replace speak.
  3. Dog("Tommy") has no __init__ of its own, so it uses Animal's.
  4. The loop calls pet.speak() without knowing which kind each one is.
  5. Python finds the right speak from the object, printing Woof and then Meow.

Expected result

Tommy says Woof
Mini says Meow

In the real world

Graphical toolkits define a general window or widget that specific controls extend. Exception classes form a tree, so catching Exception catches every kind of error built on it.

Common mistakes

  • Forgetting super().__init__(...) in a child's __init__: the parent's attributes are never set, and using them later raises an AttributeError.
  • Misspelling a method you meant to override (speek): Python quietly adds a new method, and the parent's version still runs.
  • Using inheritance for things that only have another thing. A Car has an Engine; it is not an Engine. Store it as an attribute instead.

Best practices

  • Keep inheritance hierarchies shallow.
  • Call super().__init__ in a child's __init__.
  • Prefer storing an object as an attribute when one thing merely has another.

Summary

A child class gets everything from its parent, and can add or override methods. super() reaches the parent's version. Polymorphism lets a loop treat different kinds of objects in the same way.

Practice

Add a Bird class to the animals example that overrides speak to return "Tweet", and add a bird to the loop. Then give Animal a method describe() that returns the name and the sound together.

Quick check

1. Dog(Animal) has no speak method. Which speak runs for a Dog?
2. What does super().__init__(name) do?
3. If s is a Student and Student(Person), what is isinstance(s, Person)?

Key takeaways

  • A child can reuse, add to or replace what its parent has.
  • super() lets the parent do its part.
  • Use inheritance only for a true is-a relationship.

Practice problems for this lesson

My notes

Write down what you want to remember from this lesson. Notes are saved on this device as you type, and appear on your dashboard.