Phase: 5. OOP | Estimated time: 2 hours | Milestone Project: No
- Module 045 (Encapsulation)
By the end of this module, you will be able to:
- Create a child class that inherits from a parent class
- Override methods in a subclass
- Use
super()to call parent methods - Use
isinstance()andissubclass()for type checking - Explain Method Resolution Order (MRO)
- Inherit from built-in types
Inheritance lets you define a general class and then create specialized versions. It's one of the most powerful mechanisms for code reuse and building hierarchical relationships.
A child class inherits all attributes and methods from its parent:
class Animal:
"""Base class for animals."""
def __init__(self, name):
self.name = name
def speak(self):
"""Make a generic animal sound."""
return f"{self.name} makes a sound."
class Dog(Animal):
"""Dog inherits from Animal."""
def speak(self):
"""Override with a dog-specific sound."""
return f"{self.name} says Woof!"
class Cat(Animal):
"""Cat inherits from Animal."""
def speak(self):
"""Override with a cat-specific sound."""
return f"{self.name} says Meow!"Child classes can override any method from the parent to provide specialized behavior:
class Animal:
def move(self):
return "Moving..."
class Bird(Animal):
def move(self):
return "Flying..."
class Fish(Animal):
def move(self):
return "Swimming..."
print(Bird().move()) # Flying...
print(Fish().move()) # Swimming...Call the parent's version of a method from the child:
class Vehicle:
"""Base vehicle class."""
def __init__(self, make, model, year):
self.make = make
self.model = model
self.year = year
def description(self):
"""Return vehicle description."""
return f"{self.year} {self.make} {self.model}"
class ElectricCar(Vehicle):
"""Electric car extends Vehicle."""
def __init__(self, make, model, year, battery_kwh):
super().__init__(make, model, year)
self.battery_kwh = battery_kwh
def description(self):
"""Extend parent description."""
base = super().description()
return f"{base} ({self.battery_kwh} kWh battery)"animal = Animal("Generic")
dog = Dog("Rex")
print(isinstance(dog, Dog)) # True
print(isinstance(dog, Animal)) # True (inheritance!)
print(isinstance(animal, Dog)) # False
print(issubclass(Dog, Animal)) # True
print(issubclass(Animal, Dog)) # FalsePython determines which method to call using the C3 linearization. Use ClassName.__mro__ to inspect the order.
class A: pass
class B(A): pass
class C(A): pass
class D(B, C): pass
print(D.__mro__)
# (<class 'D'>, <class 'B'>, <class 'C'>, <class 'A'>, <class 'object'>)class MutableString(list):
"""A string-like class that supports mutation."""
def __init__(self, initial=""):
super().__init__(initial)
def __str__(self):
return "".join(self)
def append(self, char):
if len(char) != 1:
raise ValueError("Only single characters allowed")
super().append(char)
ms = MutableString("hello")
ms.append("!")
print(str(ms)) # hello!- Forgetting
super().__init__(): Child__init__completely overrides parent unless you callsuper(). - Deep inheritance hierarchies: Prefer composition over inheritance for complex cases.
- Circular inheritance: Python raises
TypeErrorat class creation time. - Misunderstanding MRO: Diamond-shaped hierarchies can produce surprising results.
- Define a
class Shapewith__init__(self, color)andarea()raisingNotImplementedError. - Define
Circle(Shape)andRectangle(Shape)that implementarea(). - Write a function that takes any Shape and prints its area.
- Use
super()in the child__init__.
- Child classes inherit all parent attributes and methods.
- Override methods to specialize behavior.
super()lets you call the parent's implementation.isinstance()andissubclass()check type relationships.- MRO determines method lookup order in multiple inheritance.
Continue to Module 047: Polymorphism.