Python Inheritance
Object-Oriented Python Object-Oriented Python

Python Inheritance

1Python Inheritance: overview

Inheritance lets a new class (the child) start with all the attributes and methods of an existing class (the parent) and then add to them or change them, so shared code is written only once.

GoalBy the end you can create a child class, run the parent's init with super(), override a method, call the same method on different kinds of objects (polymorphism), check types with isinstance(), and mark internal attributes with a leading underscore.

Before this lesson: Python Functions, Python Classes and Objects

2Python Inheritance: diagram

Python Inheritanceclass Employeeparent: name, salary, yearly_pay()inheritsclass Manager(Employee)child: adds bonus, overrides yearly_pay()createsben = Manager("Ben", 4000, 5000)an object of the child classcallsben.yearly_pay()Python looks in Manager first, then Employee
A child class starts with everything its parent has, then adds or replaces pieces. When you call a method on an object, Python looks for it in the object's own class first and only then in the parent class.

3Python Inheritance: explained step by step

When several classes need the same attributes and methods, copying that code into each one makes every later change painful. Inheritance lets you write the shared part once in a parent class (also called a base class or superclass) and build child classes (subclasses) on top of it.

Creating a child class

Put the parent's name in parentheses after the child's name. The child immediately has every method of the parent, even if its own body is empty:

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

    def speak(self):
        return f"{self.name} makes a sound"

class Dog(Animal):
    pass

rex = Dog("Rex")
print(rex.speak())
print(isinstance(rex, Animal))

Overriding a method

If the child defines a method with the same name as one in the parent, the child's version wins for child objects. This is called overriding. Python searches for a method in the object's own class first and only moves up to the parent when it doesn't find it there.

Extending __init__ with super()

Often a child needs everything the parent's __init__ does plus a few extra attributes. Instead of copying the parent's lines, call super().__init__(...). It runs the parent's version on the same object; then you add your own attributes:

class Cat(Animal):
    def __init__(self, name, indoor):
        super().__init__(name)    # the parent sets self.name
        self.indoor = indoor      # the child adds its own attribute

If a child defines __init__ and forgets to call super().__init__(), the parent's setup never runs and attributes like self.name simply won't exist. You can use super() in any method, not just __init__, to build on the parent's version instead of replacing it.

Polymorphism

Polymorphism means "many forms": different classes provide a method with the same name, so one piece of code can work with all of them. The loop below doesn't care which kind of shape it gets — it just calls area(), and each object runs its own version:

Python
class Shape:
    def area(self):
        return 0

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

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

class Circle(Shape):
    def __init__(self, radius):
        self.radius = radius

    def area(self):
        return round(3.14159 * self.radius ** 2, 2)

for shape in [Square(3), Circle(1), Shape()]:
    print(type(shape).__name__, shape.area())

Checking types with isinstance()

isinstance(obj, SomeClass) returns True if obj was made from that class or from any of its children. A Square is a Shape, but a plain Shape is not a Square.

Encapsulation by convention

Python has no truly private attributes. Instead, a name that starts with one underscore, like self._balance, means "internal detail — please use the methods instead". Python won't stop anyone from touching it; it is an agreement between programmers.

When to use inheritance

Use it for genuine "is a" relationships: a Manager is an Employee. When one thing merely has another — a car has an engine — store the other object as an attribute instead.

Quick reference: Python Inheritance

4Python Inheritance: syntax

class Parent:
    def __init__(self, a):
        self.a = a

    def method(self):
        return "parent version"

class Child(Parent):                  # Child inherits from Parent
    def __init__(self, a, b):
        super().__init__(a)           # run Parent's __init__ first
        self.b = b                    # then add something new

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

isinstance(Child(1, 2), Parent)       # True

5Python Inheritance: run the example

Press Run (or Ctrl + Enter). The output (step 6) appears directly below the code. Change anything and run it again.

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

    def yearly_pay(self):
        return self.salary * 12

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

    def yearly_pay(self):
        return super().yearly_pay() + self.bonus

staff = [Employee("Asha", 3000), Manager("Ben", 4000, 5000)]
for person in staff:
    print(person.name, person.yearly_pay(), isinstance(person, Manager))
print("Is Ben an Employee?", isinstance(staff[1], Employee))
Expected outputAsha 36000 False
Ben 53000 True
Is Ben an Employee? True

7Python Inheritance: how the example works

Hover or tap a step to highlight its lines in the example above.

  1. Line 1-4The parent class Employee stores a name and a monthly salary on every new object.
  2. Line 6-7yearly_pay() returns twelve months of salary. Every employee — and every child class — gets this method.
  3. Line 9class Manager(Employee): creates a child class. A Manager automatically has everything an Employee has.
  4. Line 10-12Manager needs an extra bonus, so it defines its own __init__. super().__init__(name, salary) runs the parent's setup, then the child adds self.bonus.
  5. Line 14-15The child overrides yearly_pay(). Instead of repeating the maths, it calls the parent's version with super().yearly_pay() and adds the bonus.
  6. Line 17One list holds both kinds of objects.
  7. Line 18-19Polymorphism: the same call person.yearly_pay() runs Employee's version for Asha (36000) and Manager's version for Ben (48000 + 5000). isinstance shows which objects are managers.
  8. Line 20A Manager is also an Employee, because isinstance() counts parent classes too.

8Python Inheritance: common mistakes

Forgetting to call super().__init__()

✗ Wrong

class Vehicle:
    def __init__(self, wheels):
        self.wheels = wheels

class Bike(Vehicle):
    def __init__(self, brand):
        self.brand = brand

trek = Bike("Trek")
print(trek.wheels)
AttributeError: 'Bike' object has no attribute 'wheels'

✓ Right

class Vehicle:
    def __init__(self, wheels):
        self.wheels = wheels

class Bike(Vehicle):
    def __init__(self, brand):
        super().__init__(2)
        self.brand = brand

trek = Bike("Trek")
print(trek.wheels)

The child's __init__ replaces the parent's, so the parent's setup never ran. Call super().__init__(...) inside the child's __init__.

Passing self to super().__init__()

✗ Wrong

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

class Dog(Animal):
    def __init__(self, name):
        super().__init__(self, name)

rex = Dog("Rex")
TypeError: Animal.__init__() takes 2 positional arguments but 3 were given

✓ Right

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

class Dog(Animal):
    def __init__(self, name):
        super().__init__(name)

rex = Dog("Rex")
print(rex.name)

super() already knows which object it is working on, so self is passed automatically. Pass only the other arguments.

Misspelling the method you meant to override

✗ Wrong

class Shape:
    def area(self):
        return 0

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

    def Area(self):
        return self.side ** 2

print(Square(4).area())
Prints 0 — no error, but the parent's method ran

✓ Right

class Shape:
    def area(self):
        return 0

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

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

print(Square(4).area())

Overriding only happens when the names match exactly (Python is case-sensitive). Area is a brand-new method, so area() still comes from Shape.

9Python Inheritance: exercises

Write your answer, then press Check. The checks run in your browser.

1. Create a Cat class

Write code Easy

Create a class Cat that inherits from Animal and overrides speak() so it returns "<name> says meow" — for example "Tom says meow". Do not change Animal.

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

    def speak(self):
        return f"{self.name} makes a sound"

# create class Cat here


kitty = Cat("Tom")
print(kitty.speak())

2. Fix the savings account

Fix the bug Medium

SavingsAccount should have an owner, a balance that starts at 0 and an interest rate. The program crashes with an AttributeError. Fix the child class so the parent's setup runs.

Your code
class Account:
    def __init__(self, owner):
        self.owner = owner
        self.balance = 0

class SavingsAccount(Account):
    def __init__(self, owner, rate):
        self.rate = rate

    def add_interest(self):
        self.balance += self.balance * self.rate

savings = SavingsAccount("Asha", 0.05)
savings.balance = 200
savings.add_interest()
print(savings.owner, savings.balance)

2 more exercises → 4 more examples

Finished this lesson?
Progress is saved in this browser — create a free account to keep it across devices.

Questions learners ask

What does super() do in Python?

super() gives you access to the parent class's methods from inside a child class. super().init(...) runs the parent's setup on the current object, and super().method() calls the parent's version of a method you have overridden, so you can extend it instead of rewriting it.

When should I use inheritance instead of an attribute?

Use inheritance when the child really is a special kind of the parent (a Manager is an Employee) and should work anywhere the parent works. If one object only uses or contains another (a Car has an Engine), store it as an attribute instead; this is called composition and is often simpler.

Does Python have private attributes?

Not truly. A single leading underscore (_balance) is a convention meaning internal, please don't touch. Two leading underscores (__balance) make Python rename the attribute to _ClassName__balance, which avoids accidental clashes in child classes but still isn't real privacy.

Further reading

10Next lesson

Up nextPython Polymorphism and Duck TypingPolymorphism means different kinds of objects respond to the same method call in their own way, and in Python duck typi…

Related:
Python Polymorphism and Duck TypingPython Multiple Inheritance and MROPython Composition: Has-A vs Is-A

← Python Dunder (Magic) Methods · All Object-Oriented Python lessons · Practise in the IDE