Skip to main content

Python Inheritance

Introduction

Inheritance is one of the fundamental concepts in object-oriented programming (OOP) that allows us to create new classes based on existing ones. When a class inherits from another class, it acquires all the attributes and methods of the parent class. This promotes code reuse, establishes a hierarchy between classes, and helps in organizing your code more effectively.

In this tutorial, you'll learn:

  • What inheritance is and why it's useful
  • How to implement inheritance in Python
  • Different types of inheritance
  • Method overriding and extending parent functionality
  • Practical applications of inheritance

Understanding Inheritance

Inheritance models an "is-a" relationship. For example, a Car is a Vehicle. In this relationship:

  • The class being inherited from is called the parent class, base class, or superclass.
  • The class doing the inheriting is called the child class, derived class, or subclass.

Basic Syntax

python
class ParentClass:
# Parent class attributes and methods

class ChildClass(ParentClass):
# Child class attributes and methods
# Has access to all non-private parent attributes and methods

Creating Your First Inheritance Relationship

Let's see a simple example of inheritance in action:

python
# Parent class
class Animal:
def __init__(self, name, species):
self.name = name
self.species = species

def make_sound(self):
print("Some generic animal sound")

def info(self):
return f"I am {self.name}, a {self.species}"

# Child class
class Dog(Animal):
def __init__(self, name, breed):
# Call the parent class's __init__ method
super().__init__(name, species="Dog")
self.breed = breed

def make_sound(self):
print("Woof! Woof!")

# Create an instance of Dog
my_dog = Dog("Buddy", "Golden Retriever")

# Access methods from the parent class
print(my_dog.info()) # Output: I am Buddy, a Dog

# The child class has overridden the make_sound method
my_dog.make_sound() # Output: Woof! Woof!

# Access attributes from both classes
print(f"{my_dog.name} is a {my_dog.breed}") # Output: Buddy is a Golden Retriever

In this example, Dog inherits from Animal. It has access to the info() method from Animal, but it overrides the make_sound() method to provide its own implementation.

The super() Function

The super() function is used to call methods from the parent class. It's particularly useful when you want to extend the functionality of a parent method rather than completely replacing it.

python
class Cat(Animal):
def __init__(self, name, color):
# Call the parent's __init__ method
super().__init__(name, species="Cat")
self.color = color

def make_sound(self):
print("Meow!")

def info(self):
# Extend the parent's info method
parent_info = super().info()
return f"{parent_info} with {self.color} fur"

# Create a Cat instance
my_cat = Cat("Whiskers", "tabby")
print(my_cat.info()) # Output: I am Whiskers, a Cat with tabby fur

Types of Inheritance

Python supports different types of inheritance:

1. Single Inheritance

A child class inherits from just one parent class. This is what we've seen in the examples above.

2. Multiple Inheritance

A child class inherits from more than one parent class.

python
class Swimming:
def swim(self):
return "I can swim!"

class Flying:
def fly(self):
return "I can fly!"

# Duck inherits from both Swimming and Flying
class Duck(Swimming, Flying):
def quack(self):
return "Quack!"

donald = Duck()
print(donald.swim()) # Output: I can swim!
print(donald.fly()) # Output: I can fly!
print(donald.quack()) # Output: Quack!

3. Multilevel Inheritance

A class inherits from a child class, forming a chain:

python
class Animal:
def eat(self):
return "I can eat!"

class Dog(Animal):
def bark(self):
return "I can bark!"

class Labrador(Dog):
def color(self):
return "I am yellow or black in color!"

rocky = Labrador()
print(rocky.eat()) # Output: I can eat!
print(rocky.bark()) # Output: I can bark!
print(rocky.color()) # Output: I am yellow or black in color!

4. Hierarchical Inheritance

Multiple child classes inherit from a single parent class:

python
class Vehicle:
def general_usage(self):
return "Transportation"

class Car(Vehicle):
def specific_usage(self):
return "Family trips, commuting"

class Motorcycle(Vehicle):
def specific_usage(self):
return "Racing, quick transportation"

# Both Car and Motorcycle inherit from Vehicle
family_car = Car()
racing_bike = Motorcycle()

print(family_car.general_usage()) # Output: Transportation
print(racing_bike.general_usage()) # Output: Transportation
print(family_car.specific_usage()) # Output: Family trips, commuting
print(racing_bike.specific_usage()) # Output: Racing, quick transportation

Method Resolution Order (MRO)

When dealing with multiple inheritance, Python needs to determine the order in which it looks for methods and attributes. This order is called Method Resolution Order (MRO).

Python uses the C3 Linearization algorithm to determine the MRO. You can view the MRO for any class using the __mro__ attribute or the mro() method:

python
# Multiple inheritance example
class A:
def who_am_i(self):
return "I am A"

class B:
def who_am_i(self):
return "I am B"

class C(A, B):
pass

instance = C()
print(instance.who_am_i()) # Output: I am A
print(C.__mro__) # Shows the MRO for class C

Output:

I am A
(<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>)

As you can see, Python checked class C first, then A, then B, and finally the built-in object class.

Real-World Example: Building a Simple Game Character System

Let's apply inheritance to build a simple game character system:

python
class Character:
def __init__(self, name, health=100):
self.name = name
self.health = health
self.level = 1

def introduce(self):
return f"I am {self.name}, level {self.level} character with {self.health} health."

def level_up(self):
self.level += 1
self.health += 20
return f"{self.name} leveled up to level {self.level}!"

class Warrior(Character):
def __init__(self, name, health=150, strength=10):
super().__init__(name, health)
self.strength = strength

def introduce(self):
base_intro = super().introduce()
return f"{base_intro} I am a mighty warrior with {self.strength} strength!"

def slash(self, target):
damage = self.strength * 2
target.health -= damage
return f"{self.name} slashed {target.name} for {damage} damage!"

class Mage(Character):
def __init__(self, name, health=80, mana=100):
super().__init__(name, health)
self.mana = mana

def introduce(self):
base_intro = super().introduce()
return f"{base_intro} I am a wise mage with {self.mana} mana!"

def cast_spell(self, spell_name, target):
if self.mana >= 20:
self.mana -= 20
damage = self.level * 10
target.health -= damage
return f"{self.name} cast {spell_name} on {target.name} for {damage} damage!"
else:
return f"{self.name} doesn't have enough mana to cast {spell_name}!"

# Game simulation
arthur = Warrior("Arthur", strength=15)
merlin = Mage("Merlin", mana=150)

print(arthur.introduce())
print(merlin.introduce())

print(arthur.slash(merlin))
print(merlin.cast_spell("Fireball", arthur))

print(f"Arthur's health: {arthur.health}")
print(f"Merlin's health: {merlin.health}")

print(arthur.level_up())
print(merlin.level_up())

Output:

I am Arthur, level 1 character with 150 health. I am a mighty warrior with 15 strength!
I am Merlin, level 1 character with 80 health. I am a wise mage with 150 mana!
Arthur slashed Merlin for 30 damage!
Merlin cast Fireball on Arthur for 10 damage!
Arthur's health: 140
Merlin's health: 50
Arthur leveled up to level 2!
Merlin leveled up to level 2!

This example demonstrates how inheritance can be used to create a hierarchy of related classes while allowing each class to have its own specific attributes and behaviors.

Best Practices for Using Inheritance

  1. Follow the "is-a" relationship: A child class should represent a more specific version of its parent. If you can say "Child is a Parent," inheritance is appropriate.

  2. Prefer composition over inheritance when the relationship is "has-a" rather than "is-a". For example, a Car has an Engine, but it is not an Engine.

  3. Keep your inheritance hierarchies shallow: Deep inheritance chains can make your code hard to understand and maintain.

  4. Use the Liskov Substitution Principle: Any instance of a parent class should be replaceable with an instance of its child class without affecting the functionality of the program.

  5. Avoid multiple inheritance when possible: While Python supports it, multiple inheritance can lead to complex and confusing code. Use it sparingly and with caution.

Summary

Inheritance is a powerful feature in Python's object-oriented programming that allows classes to inherit attributes and methods from other classes. It promotes code reuse, establishes class hierarchies, and helps in organizing your code.

Key points to remember:

  • A child class inherits attributes and methods from a parent class
  • The super() function calls methods from the parent class
  • Method overriding allows child classes to provide specific implementations
  • Python supports single, multiple, multilevel, and hierarchical inheritance
  • Method Resolution Order (MRO) determines which method is called in multiple inheritance

By mastering inheritance, you'll be able to create more efficient, organized, and maintainable code in your Python projects.

Exercises

  1. Create a Vehicle class with attributes like make, model, and year. Then create child classes for specific types of vehicles like Car, Motorcycle, and Truck.

  2. Implement a Shape class hierarchy with a base Shape class and derived classes like Circle, Rectangle, and Triangle. Each should have a method to calculate area.

  3. Create a BankAccount class and then implement SavingsAccount and CheckingAccount as child classes with their specific functionalities.

  4. Implement a simple class hierarchy for a university system with classes like Person, Student, Professor, and Staff.

  5. Create a multiple inheritance example with ElectricCar that inherits from both Car and an Electric class.

Additional Resources



If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)