Introduction
In this lesson, we'll explore advanced concepts of object-oriented programming in Python, focusing on inheritance, composition, and method overriding. These concepts are crucial for creating flexible, robust, and maintainable software designs. Understanding when to use each and how to implement them properly will enable you to design better Python applications by leveraging code reuse and extensibility.
Inheritance in Python
Inheritance allows a new class (derived class) to adopt the properties and behavior of an existing class (base class). This is useful for creating hierarchical class models where generic functionality is defined in a base class and specific behavior is added in derived classes.
Implementing Single Inheritance
In Python, a class can inherit from another by simply specifying the base class in parentheses after the class name:
class Animal:
def speak(self):
return "Some sound"
class Dog(Animal):
def speak(self):
return "Woof!"
In this example, Dog inherits the speak method from Animal but overrides it to provide a different implementation.
Multilevel Inheritance
Multilevel inheritance involves a chain of inheritance where a class is derived from another derived class:
class Mammal(Animal):
def feed_milk(self):
return "Feeding milk"
class Cat(Mammal):
def purr(self):
return "Purr"
Here, Cat inherits from Mammal, which in turn inherits from Animal. Thus, Cat has access to both feed_milk and speak methods.
Method Resolution Order (MRO) and super()
Python's MRO defines the order in which base classes are looked up when searching for a method. It follows the C3 linearization algorithm, which is top-down and left-to-right.
To call a parent class method, use super():
class A:
def show(self):
print("Class A")
class B(A):
def show(self):
super().show()
print("Class B")
Using super(), B can ensure it also runs A's show method, maintaining a predictable order defined by MRO.
Composition
Composition involves constructing complex classes using simpler, existing classes. This is different from inheritance, where a child class is a type of its parent class. Composition models a "has-a" relationship rather than an "is-a" relationship.
Designing with Composition
class Engine:
def start(self):
return "Engine started"
class Car:
def __init__(self):
self.engine = Engine() # Car has an engine
def start(self):
return self.engine.start()
In this example, Car contains an Engine, but is not an engine itself.
Method Overriding
Overriding allows a derived class to provide a specific implementation of a method that is already defined in its base class. Overriding enables class extension and modification without changing external interfaces.
Customizing Behavior
class Bird:
def fly(self):
return "Flying"
class Penguin(Bird):
def fly(self):
return "Can't fly"
Penguin provides a specific implementation for fly, overriding Bird's version to reflect a penguin's real-world inability to fly.
Summary / Key Takeaways
In this lesson on Inheritance, Composition, and Method Overriding, you have learned:
- Understanding Inheritance: Use inheritance to create hierarchical structures allowing for code reuse and clear models of related classes.
- Implementing Composition: Model "has-a" relationships using composition to build complex functions from simpler objects.
- Method Overriding: Customize or extend inherited behavior in subclasses.
- Proper Use of
super(): Utilizesuper()for invoking base class methods, adhering to MRO. - Method Resolution Order (MRO): Understand MRO in Python to control method lookups correctly for predictable behavior in classes.
These concepts are key to advancing your Python programming capabilities, enabling more organized, maintainable, and scalable code designs.
