Questions or a demo for your school?Student? Sit a test
BrainBit Infotech TeachingStart free

Python notes · Topic 15 of 22 · 5 min read

Python inheritance, polymorphism and magic methods

Python inheritance notes: child classes and super(), method overriding, polymorphism, private names and @property, dunder (magic) methods and abstract classes.

Inheritance: "is-a" relationships

Inheritance lets a new class reuse an existing one. The new class (the child or subclass) gets every attribute and method of the parent (base class), and can add or change some.

Use inheritance when the child really *is a* kind of the parent: a Manager is an Employee, a Car is a Vehicle. If the relationship is "has a" (a Car *has an* Engine), store an object as an attribute instead.

class Employee:
    def __init__(self, name, salary):
        self.name = name
        self.salary = salary

    def pay(self):
        return self.salary

class Manager(Employee):                 # Manager inherits from Employee
    pass

m = Manager("Asha", 50000)
print(m.name, m.pay())                   # Asha 50000 -- inherited

Child classes with super()

class Manager(Employee):
    def __init__(self, name, salary, bonus):
        super().__init__(name, salary)   # let the parent set name and salary
        self.bonus = bonus               # then add what is new

    def pay(self):                       # override the parent's method
        return super().pay() + self.bonus

m = Manager("Asha", 50000, 10000)
print(m.pay())                           # 60000
  • super() gives you the parent class, so you can call its version of a method.
  • Overriding means writing a method in the child with the same name as one in the parent. The child's version wins.

Which method runs?

When you call m.pay(), Python looks for pay in Manager first. If it is not there, it looks in Employee, then in object (the parent of every class). The first one found runs. This search order is called the method resolution order, and Manager.__mro__ shows it.

isinstance(m, Employee) is True: a Manager counts as an Employee.

Polymorphism: one interface, many forms

Polymorphism means different classes can be used the same way, because they share method names.

class Auto:
    def fare(self, km):
        return 30 + km * 15

class Cab:
    def fare(self, km):
        return 50 + km * 20

class Bike:
    def fare(self, km):
        return 20 + km * 8

for ride in [Auto(), Cab(), Bike()]:
    print(type(ride).__name__, ride.fare(5))

The loop does not care which class each ride is; it only needs a fare() method. Adding a new vehicle needs no change to the loop.

Protecting data: _private names and @property

Python has no truly private attributes. By convention, a name starting with _ means "internal: do not touch from outside".

@property lets you check values while keeping simple obj.attribute syntax:

class Account:
    def __init__(self, balance):
        self._balance = balance

    @property
    def balance(self):                  # read: acc.balance
        return self._balance

    @balance.setter
    def balance(self, value):           # write: acc.balance = value
        if value < 0:
            raise ValueError("balance cannot be negative")
        self._balance = value

acc = Account(100)
acc.balance = 50          # goes through the setter's check
acc.balance = -5          # ValueError

Dunder (magic) methods

Methods with double underscores on both sides (dunder methods) let your objects work with Python's operators and built-in functions.

You writePython calls
print(a)a.__str__()
a + ba.__add__(b)
a == ba.__eq__(b)
a < ba.__lt__(b)
len(a)a.__len__()
class Money:
    def __init__(self, rupees):
        self.rupees = rupees

    def __add__(self, other):
        return Money(self.rupees + other.rupees)

    def __lt__(self, other):
        return self.rupees < other.rupees

    def __str__(self):
        return f"Rs {self.rupees}"

print(Money(100) + Money(250))     # Rs 350
print(Money(100) < Money(250))     # True

Defining __lt__ also lets sorted() and min() work on a list of Money objects.

Abstract base classes

An abstract class is a parent that only describes which methods its children must have. You cannot create objects from it directly.

from abc import ABC, abstractmethod

class Payment(ABC):
    @abstractmethod
    def pay(self, amount):
        ...

class UPI(Payment):
    def pay(self, amount):
        print(f"Paid Rs {amount} by UPI")

UPI().pay(200)
Payment()        # TypeError: can't instantiate abstract class

A child that forgets to write pay() also cannot be created, so the mistake is caught early.

Common mistakes

  • Forgetting super().__init__(...) in the child, so the parent's attributes are never set.
  • Using inheritance for a "has-a" relationship.
  • Overriding a method with a misspelt name, so the parent's version still runs.
  • Writing __add__ that changes self instead of returning a new object.

Key points

  • A child class inherits from its parent and can override methods.
  • super() calls the parent's version of a method.
  • Polymorphism: different classes, same method names, used the same way.
  • Dunder methods connect your classes to +, ==, <, len() and print().

Practice questions

1. What does super().__init__(name) do inside a child's __init__?

It runs the parent's __init__, so the attributes the parent sets up are created on the child object too.

2. Dog inherits from Animal. Both have speak(). Which runs for Dog().speak()?

Dog's version, because Python looks in the child class first.

3. Write Circle and Square classes with an area() method, and print both areas in one loop.
class Circle:
    def __init__(self, r): self.r = r
    def area(self): return 3.14 * self.r ** 2

class Square:
    def __init__(self, s): self.s = s
    def area(self): return self.s ** 2

for shape in [Circle(2), Square(3)]:
    print(shape.area())
4. Which dunder method makes len(obj) work?

__len__, which must return a whole number.

5. Why can you not create an object from an abstract class?

It has abstract methods with no real code. Python refuses, so that only complete child classes are used.