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
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:
# 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.
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.
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:
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:
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:
# 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:
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
-
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.
-
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.
-
Keep your inheritance hierarchies shallow: Deep inheritance chains can make your code hard to understand and maintain.
-
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.
-
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
-
Create a
Vehicle
class with attributes likemake
,model
, andyear
. Then create child classes for specific types of vehicles likeCar
,Motorcycle
, andTruck
. -
Implement a
Shape
class hierarchy with a baseShape
class and derived classes likeCircle
,Rectangle
, andTriangle
. Each should have a method to calculate area. -
Create a
BankAccount
class and then implementSavingsAccount
andCheckingAccount
as child classes with their specific functionalities. -
Implement a simple class hierarchy for a university system with classes like
Person
,Student
,Professor
, andStaff
. -
Create a multiple inheritance example with
ElectricCar
that inherits from bothCar
and anElectric
class.
Additional Resources
- Python Official Documentation on Classes
- Real Python's Guide to Inheritance and Composition
- Method Resolution Order in Python
- Book: "Python Object-Oriented Programming" by Steven F. Lott
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)