Learn by reading through in order

Class Inheritance — Reusing Features from an Existing Class

Learn Python class inheritance. Inherit attributes and methods with class Child(Parent):, override methods, and call the parent's version with super() — all with diagrams.

Last time we used special methods like __add__ and __str__ to teach built-in operations to our own classes. This time we'll cover one of the core ideas in OOP: class inheritance.

Why Use Inheritance?

When you build apps, you often run into classes that are mostly the same but differ in a few spots. Imagine Customer, Employee, and Admin — they all hold a name and email and return a greeting(), but each one has a few unique attributes or methods on top.

You could write the same name / email / greeting() in all three classes — but the code would be duplicated and every change would have to happen in three places. The cleaner solution is to put the shared parts in a parent class and have each child class "inherit from the parent and only write the differences." That's inheritance.

Pulling Shared Code Up Into the Parent
Person(parent)name / agegreeting()Employee(child)inherits
Shared attributes and methods live in the parent class (Person). Only the differences go in the child (Employee). No need to redeclare name or greeting every time.

Inheriting from a Parent — class Child(Parent):

The syntax is simple: when defining a subclass, put the parent class in parentheses after the class name. The subclass automatically picks up all of the parent's attributes and methods.

Below, Person is the parent (with name, age, and a greeting) and Employee is the child. Employee doesn't define a single method of its own, but calling greeting() on it just works thanks to inheritance.

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

    def greeting(self):
        print(f"Hello, {self.name}")

    def say_age(self):
        print(f"{self.age} years old")


class Employee(Person):       # inherits from Person
    pass                       # body can be empty


e = Employee("Alice", 45)
e.greeting()                  # Hello, Alice   <- parent's method
e.say_age()                   # 45 years old
The Child Gets Everything the Parent Has
Person (parent)
  • Attributes: name / age
  • Methods: greeting() / say_age()
  • __init__(self, name, age)
Employee (child)
  • Body is empty (just pass)
  • Inherits parent's attributes and methods
  • Employee("Alice", 45) calls the parent's __init__
Even when the child is empty, everything the parent has is available. Reusing behavior without rewriting is the whole point of inheritance.

Build a Person parent and an Employee that does nothing more than inherit from it.

① Define class Person: with __init__(self, name, age) that assigns self.name and self.age.

② Inside Person, define greeting(self) that runs print(f"Hello, {self.name}").

③ Write class Employee(Person): with just pass for the body.

④ Build e = Employee("Alice", 45) and call e.greeting().

(Once it runs correctly, the explanation will appear.)

Python Editor

Run code to see output

Method Overriding — Replacing with the Same Name

Pure inheritance gives you the parent's behavior unchanged. But if you write a method with the same name in the child class, the child's version takes priority. That's an override.

For example, to give Employee a different greeting like "Hello, Employee Alice", redefine greeting in the child. Calling it on a Person instance gives the parent's greeting; calling it on an Employee instance gives the child's. The behavior switches based on which class the calling instance belongs to.

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

    def greeting(self):
        print(f"Hello, {self.name}")


class Employee(Person):
    def greeting(self):                # same name = override
        print(f"Hello, Employee {self.name}")


p = Person("Bob", 30)
e = Employee("Alice", 45)
p.greeting()    # Hello, Bob
e.greeting()    # Hello, Employee Alice    <- child's version wins
Method Lookup Order
e.greeting()greeting onEmployee?found→ rungreeting onPerson?run if there,else error① lookYesif No
Python looks for the method on the child class first. If it's not there, it walks up to the parent. Same name → child wins — that's overriding.

Add an override of greeting to the previous code.

① Build the same Person class as before (__init__ and greeting).

② Define class Employee(Person): and inside it redefine greeting(self) to run print(f"Hello, Employee {self.name}").

③ Build a Person("Bob", 30) and an Employee("Alice", 45), then call greeting() on each and compare.

Python Editor

Run code to see output

super() — Calling the Parent's Method

Sometimes you want to override a method but still build on the parent's behavior. That's where super() comes in. Writing super().method() calls the same-named method on the current class's parent.

For example, suppose you want "the parent's greeting (which prints Hello, Alice) followed by an extra line in the child." Inside the child's greeting, call super().greeting() first, then add your own line.

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

    def greeting(self):
        print(f"Hello, {self.name}")


class Employee(Person):
    def greeting(self):
        super().greeting()                            # ① run parent's greeting first
        print(f"I'm Employee {self.name}")            # ② then child's own line


Employee("Alice", 45).greeting()
# Hello, Alice
# I'm Employee Alice
super().greeting() Reuses the Parent's Logic
Employee.greeting()child greetingbodysuper().greeting()runs parent'sgreetingrest ofchild bodycalledruns
Inside the child's greeting, calling super().greeting() runs the parent's greeting directly. self is passed automatically — you don't write it.

Overriding __init__ to Add More Attributes

Where super() shines most is when you override __init__. Imagine the child class needs the parent's attributes plus a few of its own — for example, Employee should also hold a phone_number.

In that case you redefine __init__ in the child, but the parent's initialization (self.name = name etc.) is handled in one line by super().__init__(name, age), and you add the extra attributes after. That way you don't have to repeat the name / age assignments.

Child __init__ Builds Both Parent and Child Attributes
Employee("Alice", 45, "555-1111")child __init__runssuper().__init__(name, age)parent assignsname / ageself.phone_number= phone_numberparent + child attrsreadycalled
Calling Employee(...) runs the child's __init__super().__init__(name, age) lets the parent assign name / age → then the child adds phone_number. Final state: parent + child attributes all in place.
class Person:
    def __init__(self, name, age):
        self.name = name
        self.age  = age


class Employee(Person):
    def __init__(self, name, age, phone_number):
        super().__init__(name, age)               # ① parent sets name / age
        self.phone_number = phone_number           # ② add the child-only attr

    def show(self):
        print(f"{self.name} / {self.age} / {self.phone_number}")


Employee("Alice", 45, "555-1111").show()
# Alice / 45 / 555-1111

Forget super().__init__(...) and the Attributes Vanish

If you override __init__ but forget to call super().__init__(...), the parent's assignments like self.name = name never happen. The next time you read self.name, you get an AttributeError. Whenever you override __init__ in a child class, call super().__init__(...) right at the top — make it a habit.

Add a phone_number to Employee by overriding __init__.

① Define class Person: with __init__(self, name, age) that assigns self.name and self.age.

② Define class Employee(Person): with __init__(self, name, age, phone_number). Inside, call super().__init__(name, age) first, then self.phone_number = phone_number.

③ Inside Employee, define show(self) that runs print(f"{self.name} / {self.age} / {self.phone_number}").

④ Build Employee("Alice", 45, "555-1111") and call show().

Python Editor

Run code to see output
QUIZ

Knowledge Check

Answer each question one by one.

Q1Which of the following is the correct way to make Dog inherit from Animal?

Q2Which is the most accurate description of method overriding?

Q3If you forget to call super().__init__(...) in a child's __init__, what happens?