Python Inheritance
Inheritance
Inheritance is a core object-oriented programming concept that lets one class reuse the properties and methods of another class. Instead of writing the same code again, a new class can inherit everything from an existing class and then add or change only what is different.
For example, every manager in a company is also an employee. A Manager class can inherit the name, department, and display logic from an Employee class, and then add manager-specific features such as a team size.
Key Terms
| Term | Also Called | Meaning |
|---|---|---|
| Base class | Parent class, superclass | The class whose features are inherited |
| Derived class | Child class, subclass | The class that inherits from another class |
Inheritance models an "is-a" relationship: a Manager is an Employee, and a Car is a Vehicle. If the sentence "X is a Y" does not make sense, inheritance is probably not the right design.
Why Use Inheritance?
- Code reuse — shared logic is written once in the parent class.
- Less duplication — fixing a bug in the parent fixes it for every child.
- Logical organization — related classes form a clear hierarchy.
- Extensibility — new classes can be added by extending existing ones.
Creating a Base Class
Any normal Python class can serve as a base class.
Example: An Employee Base Class
class Employee:
def __init__(self, name, department):
self.name = name
self.department = department
def show_details(self):
print("Name:", self.name)
print("Department:", self.department)
# Create an object of Employee
emp1 = Employee("Aarav", "IT")
emp1.show_details()
Expected output:
Name: Aarav
Department: IT
Creating a Derived Class
To create a child class, put the parent class name in parentheses after the child class name.
Syntax
class ChildClass(ParentClass):
# class body
Example: A Manager Class That Inherits From Employee
class Manager(Employee):
pass
Note:
passis used here because the class adds nothing new yet. The class body cannot be empty.
Even with an empty body, Manager already has everything that Employee has — including __init__() and show_details():
mgr1 = Manager("Neha", "HR")
mgr1.show_details()
Expected output:
Name: Neha
Department: HR
How Python finds the method: When you call mgr1.show_details(), Python first looks in the Manager class. It does not find show_details() there, so it looks in the parent class, Employee, finds it, and runs it with self set to mgr1.
Checking Inheritance Relationships
print(isinstance(mgr1, Manager))
print(isinstance(mgr1, Employee))
print(issubclass(Manager, Employee))
Expected output:
True
True
True
A Manager object is also considered an Employee, because Manager inherits from Employee.
Adding an init() Method to the Child Class
If a child class defines its own __init__(), it overrides (replaces) the parent's __init__(). The parent's initializer will no longer run automatically, so the parent's attributes would not be created.
To keep the parent's setup, call the parent's __init__() explicitly from the child's __init__().
Example: Calling the Parent's init() by Name
class Manager(Employee):
def __init__(self, name, department):
Employee.__init__(self, name, department)
mgr = Manager("Neha", "HR")
mgr.show_details()
Expected output:
Name: Neha
Department: HR
Calling Employee.__init__(self, name, department) makes sure the inherited attributes name and department are initialized correctly. Notice that self must be passed explicitly when calling the method through the class name.
What Happens If You Forget?
class Manager(Employee):
def __init__(self, name, department):
pass # The parent's __init__() is never called
mgr = Manager("Neha", "HR")
mgr.show_details()
Output:
AttributeError: 'Manager' object has no attribute 'name'
The child's __init__() replaced the parent's, so self.name was never set.
Using the super() Function
The built-in super() function gives a cleaner way to call methods of the parent class.
Example: Rewriting the Initializer With super()
class Manager(Employee):
def __init__(self, name, department):
super().__init__(name, department)
super() returns a reference to the parent class, so super().__init__(name, department) calls Employee.__init__().
Advantages of super():
- You do not need to write the parent class name, so renaming the parent requires no change here.
- You do not pass
self—super()handles it. - It works correctly with more complex hierarchies, such as multiple inheritance.
Adding New Properties to the Child Class
A child class can add its own properties alongside the inherited ones.
Example: Adding a team_size Property
class Manager(Employee):
def __init__(self, name, department, team_size):
super().__init__(name, department)
self.team_size = team_size
mgr2 = Manager("Rohan", "Finance", 8)
print(mgr2.name)
print(mgr2.department)
print(mgr2.team_size)
Expected output:
Rohan
Finance
8
What happens:
Manager("Rohan", "Finance", 8)callsManager.__init__().super().__init__(name, department)letsEmployeesetnameanddepartment.self.team_size = team_sizeadds the manager-specific property.
A plain Employee object does not have team_size — only Manager objects do.
Adding New Methods to the Child Class
A child class can also define methods that exist only in the child.
Example: Adding a greet_team() Method
class Manager(Employee):
def __init__(self, name, department, team_size):
super().__init__(name, department)
self.team_size = team_size
def greet_team(self):
print(
"Hello, I am", self.name,
"from", self.department,
"department managing a team of",
self.team_size, "members."
)
mgr2 = Manager("Rohan", "Finance", 8)
mgr2.greet_team()
mgr2.show_details()
Expected output:
Hello, I am Rohan from Finance department managing a team of 8 members.
Name: Rohan
Department: Finance
mgr2 can use both its own method (greet_team()) and the inherited one (show_details()).
Overriding Parent Methods
If a child class defines a method with the same name as a parent method, the child's version is used instead. This is called method overriding.
You can also combine the parent's behavior with new behavior by calling the parent method through super():
class Manager(Employee):
def __init__(self, name, department, team_size):
super().__init__(name, department)
self.team_size = team_size
def show_details(self):
super().show_details() # Reuse the parent's output
print("Team size:", self.team_size) # Add new information
mgr3 = Manager("Priya", "Sales", 12)
mgr3.show_details()
Expected output:
Name: Priya
Department: Sales
Team size: 12
The child's show_details() runs the parent's version first and then adds a line. Overriding is the basis of polymorphism, covered in the next lesson.
Types of Inheritance
| Type | Description | Example |
|---|---|---|
| Single | One child inherits from one parent | Manager(Employee) |
| Multilevel | A child inherits from a class that is itself a child | SeniorManager(Manager), where Manager(Employee) |
| Hierarchical | Several children inherit from the same parent | Manager(Employee) and Intern(Employee) |
| Multiple | One child inherits from more than one parent | class C(A, B): |
Every class in Python ultimately inherits from the built-in class object, even when no parent is written.
Common Mistakes
| Mistake | Problem |
|---|---|
Defining __init__() in the child without calling the parent's | Inherited attributes are missing (AttributeError) |
Passing self to super().__init__() | TypeError — super() passes it automatically |
Forgetting self when calling Parent.__init__() directly | TypeError: missing argument |
| Using inheritance for "has-a" relationships | A Car has an Engine; it is not an engine — use an attribute instead |
| Misspelling an overriding method name | A new method is created, and the parent's version still runs |
Related Concepts
- Python Classes and Objects — creating the classes you inherit from
- Python
__init__()Method — initialization in parent and child classes - Python Polymorphism — using overridden methods through a common interface
- Python Encapsulation — how private attributes behave in subclasses