Learn Python multiple inheritance in 8 minutes! 🐟 (8:12)
In this chapter, you'll explore multiple inheritance - a powerful but complex OOP feature where a class can inherit from more than one parent class. You'll learn how Python's Method Resolution Order (MRO) determines which parent's method gets called, understand the diamond problem, implement mixins for code reuse, and know when to choose multiple inheritance versus composition.
- Create classes that inherit from multiple parent classes
- Understand Method Resolution Order (MRO) and the C3 linearization algorithm
- Recognize and handle the diamond problem
- Implement mixin classes for adding functionality
- Use
super()correctly in multiple inheritance scenarios - Decide when multiple inheritance is appropriate vs composition
Multiple Inheritance allows a class to inherit from more than one parent class.
# Single inheritance (normal)
class Child(Parent):
pass
# Multiple inheritance
class Child(Parent1, Parent2, Parent3):
passReal-world analogy:
A smartphone is both a phone AND a camera AND a music player - it has features from multiple "parent" devices.
class Flyer:
def fly(self):
print("Flying in the air")
class Swimmer:
def swim(self):
print("Swimming in water")
class Duck(Flyer, Swimmer): # Inherits from both!
pass
duck = Duck()
duck.fly() # From Flyer
duck.swim() # From SwimmerMRO determines which parent's method gets called when there are conflicts.
Python uses C3 Linearization to create a predictable order.
class A:
def method(self):
print("A's method")
class B:
def method(self):
print("B's method")
class C(A, B): # A comes first
pass
c = C()
c.method() # A's method (A is checked first)
# View MRO
print(C.mro())
# [<class 'C'>, <class 'A'>, <class 'B'>, <class 'object'>]MRO Search Order:
- The class itself
- First parent (left to right)
- Second parent (left to right)
- ... and so on
- The
objectclass (all classes inherit from object)
Diamond Problem occurs when a class inherits from two classes that both inherit from a common parent.
Animal
/ \
Flyer Swimmer
\ /
Duckclass Animal:
def __init__(self):
print("Animal init")
class Flyer(Animal):
def __init__(self):
print("Flyer init")
super().__init__()
class Swimmer(Animal):
def __init__(self):
print("Swimmer init")
super().__init__()
class Duck(Flyer, Swimmer):
def __init__(self):
print("Duck init")
super().__init__()
duck = Duck()
# Output:
# Duck init
# Flyer init
# Swimmer init
# Animal init (only called ONCE!)
# MRO ensures Animal.__init__ is called only once
print(Duck.mro())
# [Duck, Flyer, Swimmer, Animal, object]Key Point: Python's MRO ensures each class's __init__ is called only once, even in diamond inheritance!
ALWAYS use super() instead of calling parent classes directly:
❌ Wrong:
class Duck(Flyer, Swimmer):
def __init__(self):
Flyer.__init__(self) # Might call Animal twice!
Swimmer.__init__(self)✅ Correct:
class Duck(Flyer, Swimmer):
def __init__(self):
super().__init__() # Follows MRO correctlyMixin = A class designed to add specific functionality through multiple inheritance.
Mixins usually:
- Don't have their own
__init__ - Provide specific methods
- Are combined with other classes
class JSONMixin:
"""Add JSON export capability."""
def to_json(self):
import json
return json.dumps(self.__dict__)
class XMLMixin:
"""Add XML export capability."""
def to_xml(self):
items = ''.join(f'<{k}>{v}</{k}>' for k, v in self.__dict__.items())
return f'<object>{items}</object>'
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
# Add JSON and XML export to Person
class ExportablePerson(Person, JSONMixin, XMLMixin):
pass
person = ExportablePerson("Alice", 30)
print(person.to_json()) # {"name": "Alice", "age": 30}
print(person.to_xml()) # <object><name>Alice</name><age>30</age></object>✅ Good use cases:
- Mixins for adding features (logging, serialization)
- Interface-like behavior (Flyable, Swimmable)
- Small, well-defined capabilities
❌ Avoid when:
- Creates complex diamond hierarchies
- Parents have conflicting methods
- Composition would be clearer
Alternative: Composition
# Instead of: class Duck(Flyer, Swimmer)
class Duck:
def __init__(self):
self.flight = Flyer() # Has-a relationship
self.swimming = Swimmer()
def fly(self):
self.flight.fly()
def swim(self):
self.swimming.swim()class Animal:
"""Base animal class."""
def __init__(self, name):
self.name = name
def eat(self):
print(f"{self.name} is eating")
class Flyer:
"""Flying capability."""
def fly(self):
print(f"{self.name} is flying")
class Swimmer:
"""Swimming capability."""
def swim(self):
print(f"{self.name} is swimming")
# Duck can do both!
class Duck(Animal, Flyer, Swimmer):
pass
# Fish can only swim
class Fish(Animal, Swimmer):
pass
# Bird can only fly
class Bird(Animal, Flyer):
pass
# Test
duck = Duck("Donald")
duck.eat() # From Animal
duck.fly() # From Flyer
duck.swim() # From Swimmer
fish = Fish("Nemo")
fish.eat()
fish.swim()
# fish.fly() # ERROR - Fish can't fly
print(f"\nDuck MRO: {[cls.__name__ for cls in Duck.mro()]}")
# Duck MRO: ['Duck', 'Animal', 'Flyer', 'Swimmer', 'object']import json
import pickle
class Person:
"""Basic person class."""
def __init__(self, name, age, email):
self.name = name
self.age = age
self.email = email
def __repr__(self):
return f"Person('{self.name}', {self.age}, '{self.email}')"
class JSONSerializableMixin:
"""Add JSON serialization."""
def to_json(self):
"""Convert to JSON string."""
return json.dumps(self.__dict__, indent=2)
@classmethod
def from_json(cls, json_str):
"""Create instance from JSON string."""
data = json.loads(json_str)
return cls(**data)
class PickleSerializableMixin:
"""Add pickle serialization."""
def to_pickle(self):
"""Convert to pickle bytes."""
return pickle.dumps(self)
@classmethod
def from_pickle(cls, pickle_bytes):
"""Create instance from pickle bytes."""
return pickle.loads(pickle_bytes)
class DictMixin:
"""Add dictionary conversion."""
def to_dict(self):
"""Convert to dictionary."""
return self.__dict__.copy()
def update_from_dict(self, data):
"""Update attributes from dictionary."""
for key, value in data.items():
setattr(self, key, value)
# Combine Person with all mixins
class SerializablePerson(Person, JSONSerializableMixin,
PickleSerializableMixin, DictMixin):
"""Person with serialization capabilities."""
pass
# Test all serialization methods
person = SerializablePerson("Alice", 30, "[email protected]")
# JSON
json_str = person.to_json()
print("JSON:")
print(json_str)
# Dictionary
person_dict = person.to_dict()
print("\nDictionary:")
print(person_dict)
# Pickle
pickle_bytes = person.to_pickle()
print(f"\nPickle: {len(pickle_bytes)} bytes")
# Restore from JSON
restored = SerializablePerson.from_json(json_str)
print(f"\nRestored from JSON: {restored}")class Vehicle:
"""Base vehicle class."""
def __init__(self, name):
self.name = name
print(f"Vehicle.__init__({name})")
def move(self):
print(f"{self.name} is moving")
class LandVehicle(Vehicle):
"""Land-based vehicle."""
def __init__(self, name, wheels):
print(f"LandVehicle.__init__({name}, {wheels})")
super().__init__(name)
self.wheels = wheels
def drive(self):
print(f"{self.name} is driving on {self.wheels} wheels")
class WaterVehicle(Vehicle):
"""Water-based vehicle."""
def __init__(self, name, displacement):
print(f"WaterVehicle.__init__({name}, {displacement})")
super().__init__(name)
self.displacement = displacement
def sail(self):
print(f"{self.name} is sailing ({self.displacement} tons)")
class AmphibiousVehicle(LandVehicle, WaterVehicle):
"""Can operate on land and water."""
def __init__(self, name, wheels, displacement):
print(f"AmphibiousVehicle.__init__({name})")
LandVehicle.__init__(self, name, wheels)
WaterVehicle.__init__(self, name, displacement)
def transform(self, mode):
"""Switch between land and water mode."""
if mode == "land":
print(f"{self.name} transforming to land mode")
self.drive()
elif mode == "water":
print(f"{self.name} transforming to water mode")
self.sail()
# Create amphibious vehicle
amphibious = AmphibiousVehicle("Duck Boat", 4, 5)
print("\n--- Testing ---")
amphibious.move()
amphibious.transform("land")
amphibious.transform("water")
print(f"\n--- MRO ---")
for cls in AmphibiousVehicle.mro():
print(cls.__name__)from datetime import datetime
class LoggingMixin:
"""Add logging capability."""
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.logs = []
def log(self, message):
"""Add log entry."""
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
entry = f"[{timestamp}] {message}"
self.logs.append(entry)
print(entry)
def show_logs(self):
"""Display all logs."""
print("\n=== Logs ===")
for log in self.logs:
print(log)
class ValidationMixin:
"""Add validation capability."""
def validate_positive(self, value, name):
"""Ensure value is positive."""
if value <= 0:
raise ValueError(f"{name} must be positive")
def validate_string(self, value, name):
"""Ensure value is non-empty string."""
if not isinstance(value, str) or not value.strip():
raise ValueError(f"{name} must be a non-empty string")
def validate_range(self, value, name, min_val, max_val):
"""Ensure value is in range."""
if not min_val <= value <= max_val:
raise ValueError(f"{name} must be between {min_val} and {max_val}")
class BankAccount:
"""Basic bank account."""
def __init__(self, owner, balance=0):
self.owner = owner
self.balance = balance
class SecureBankAccount(BankAccount, LoggingMixin, ValidationMixin):
"""Bank account with logging and validation."""
def __init__(self, owner, balance=0):
# Validate inputs
self.validate_string(owner, "Owner name")
self.validate_positive(balance if balance > 0 else 1, "Initial balance")
# Initialize parent classes
super().__init__(owner, balance)
# Log creation
self.log(f"Account created for {owner} with balance ${balance}")
def deposit(self, amount):
"""Deposit with validation and logging."""
self.validate_positive(amount, "Deposit amount")
self.balance += amount
self.log(f"Deposited ${amount}. New balance: ${self.balance}")
def withdraw(self, amount):
"""Withdraw with validation and logging."""
self.validate_positive(amount, "Withdrawal amount")
if amount > self.balance:
self.log(f"Failed withdrawal of ${amount} - insufficient funds")
raise ValueError("Insufficient funds")
self.balance -= amount
self.log(f"Withdrew ${amount}. New balance: ${self.balance}")
# Test
account = SecureBankAccount("Alice", 1000)
account.deposit(500)
account.withdraw(200)
try:
account.withdraw(5000) # Will fail
except ValueError as e:
print(f"\nError: {e}")
account.show_logs()class Component:
"""Base UI component."""
def __init__(self, name):
self.name = name
self.visible = True
def show(self):
self.visible = True
print(f"{self.name} is now visible")
def hide(self):
self.visible = False
print(f"{self.name} is now hidden")
class Clickable:
"""Add click functionality."""
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.on_click = None
def set_click_handler(self, handler):
"""Set click event handler."""
self.on_click = handler
def click(self):
"""Trigger click event."""
if self.on_click:
print(f"{self.name} clicked!")
self.on_click()
else:
print(f"{self.name} has no click handler")
class Draggable:
"""Add drag functionality."""
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.position = (0, 0)
def drag_to(self, x, y):
"""Drag to new position."""
self.position = (x, y)
print(f"{self.name} dragged to ({x}, {y})")
class Resizable:
"""Add resize functionality."""
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.width = 100
self.height = 100
def resize(self, width, height):
"""Resize component."""
self.width = width
self.height = height
print(f"{self.name} resized to {width}x{height}")
# Different component types
class Button(Component, Clickable):
"""Clickable button."""
pass
class Window(Component, Draggable, Resizable):
"""Draggable and resizable window."""
pass
class IconButton(Component, Clickable, Draggable):
"""Button that can be clicked and dragged."""
pass
# Test components
button = Button("Submit Button")
button.set_click_handler(lambda: print("Form submitted!"))
button.click()
window = Window("Main Window")
window.resize(800, 600)
window.drag_to(100, 100)
icon = IconButton("Settings Icon")
icon.set_click_handler(lambda: print("Settings opened!"))
icon.drag_to(50, 50)
icon.click()
print(f"\n--- Button MRO ---")
print([cls.__name__ for cls in Button.mro()])
print(f"\n--- IconButton MRO ---")
print([cls.__name__ for cls in IconButton.mro()])class User:
"""Base user class."""
def __init__(self, username, email):
self.username = username
self.email = email
self.is_active = True
def deactivate(self):
self.is_active = False
class CanRead:
"""Read permission."""
def read(self, document):
print(f"{self.username} reading: {document}")
class CanWrite:
"""Write permission."""
def write(self, document, content):
print(f"{self.username} writing to: {document}")
class CanDelete:
"""Delete permission."""
def delete(self, document):
print(f"{self.username} deleting: {document}")
class CanManageUsers:
"""User management permission."""
def create_user(self, username):
print(f"{self.username} created user: {username}")
def delete_user(self, username):
print(f"{self.username} deleted user: {username}")
# Different user roles
class Viewer(User, CanRead):
"""Can only read."""
pass
class Editor(User, CanRead, CanWrite):
"""Can read and write."""
pass
class Admin(User, CanRead, CanWrite, CanDelete, CanManageUsers):
"""Full permissions."""
pass
# Test roles
viewer = Viewer("john", "[email protected]")
viewer.read("report.pdf")
# viewer.write("report.pdf", "text") # ERROR - no write permission
editor = Editor("jane", "[email protected]")
editor.read("document.txt")
editor.write("document.txt", "Updated content")
# editor.delete("document.txt") # ERROR - no delete permission
admin = Admin("admin", "[email protected]")
admin.read("config.yaml")
admin.write("config.yaml", "new settings")
admin.delete("old_file.txt")
admin.create_user("newuser")
print(f"\n--- Admin Capabilities ---")
print(f"Admin MRO: {[cls.__name__ for cls in Admin.mro()]}")class Device:
"""Base device class."""
def __init__(self, name, brand):
self.name = name
self.brand = brand
self.is_on = False
def power_on(self):
self.is_on = True
print(f"{self.name} powered on")
def power_off(self):
self.is_on = False
print(f"{self.name} powered off")
class WiFiEnabled:
"""WiFi capability."""
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.connected_network = None
def connect_wifi(self, network):
self.connected_network = network
print(f"{self.name} connected to {network}")
def disconnect_wifi(self):
print(f"{self.name} disconnected from {self.connected_network}")
self.connected_network = None
class BluetoothEnabled:
"""Bluetooth capability."""
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.paired_devices = []
def pair_bluetooth(self, device):
self.paired_devices.append(device)
print(f"{self.name} paired with {device}")
def unpair_bluetooth(self, device):
self.paired_devices.remove(device)
print(f"{self.name} unpaired from {device}")
class VoiceControlled:
"""Voice control capability."""
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.wake_word = "Hey Device"
def voice_command(self, command):
if self.is_on:
print(f"{self.name} executing: {command}")
else:
print(f"{self.name} is off")
class Battery:
"""Battery capability."""
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.battery_level = 100
def check_battery(self):
print(f"{self.name} battery: {self.battery_level}%")
def charge(self):
self.battery_level = 100
print(f"{self.name} fully charged")
# Smart devices with different capabilities
class SmartPhone(Device, WiFiEnabled, BluetoothEnabled,
VoiceControlled, Battery):
"""Full-featured smartphone."""
pass
class SmartSpeaker(Device, WiFiEnabled, BluetoothEnabled, VoiceControlled):
"""Smart speaker (no battery, always plugged in)."""
pass
class SmartWatch(Device, BluetoothEnabled, Battery):
"""Smart watch (no WiFi, has battery)."""
pass
# Test devices
print("=== Smartphone ===")
phone = SmartPhone("iPhone", "Apple")
phone.power_on()
phone.connect_wifi("Home WiFi")
phone.pair_bluetooth("AirPods")
phone.voice_command("Call Mom")
phone.check_battery()
print("\n=== Smart Speaker ===")
speaker = SmartSpeaker("Echo", "Amazon")
speaker.power_on()
speaker.connect_wifi("Home WiFi")
speaker.voice_command("Play music")
# speaker.check_battery() # ERROR - no battery
print("\n=== Smart Watch ===")
watch = SmartWatch("Apple Watch", "Apple")
watch.power_on()
watch.pair_bluetooth("iPhone")
# watch.connect_wifi("WiFi") # ERROR - no WiFi
watch.check_battery()
print(f"\n=== Smartphone MRO ===")
for cls in SmartPhone.mro():
print(cls.__name__)-
Flying Animals
- Create
Animal,Flyer,Swimmerclasses - Create
Duck,Penguin,Eaglewith appropriate inheritance - Test which animals can fly/swim
- Create
-
Media Player
- Create
VideoPlayer,AudioPlayerbase classes - Create
MultimediaPlayerinheriting from both - Implement play methods
- Create
-
Shape Properties
- Create
Colored,Texturedmixin classes - Create
ColoredCircle,TexturedSquareclasses - Display properties
- Create
-
Student Roles
- Create
Student,Athlete,Scholarclasses - Create
ScholarAthletecombining both - Track activities
- Create
-
File Permissions
- Create
Readable,Writable,Executablemixins - Create different file types
- Test permissions
- Create
-
Smart Home System
- Create device base class
- Add WiFi, Bluetooth, Voice mixins
- Create different smart devices
- Implement control systems
-
Game Character Skills
- Create skill mixins (Magic, Archery, Sword)
- Create character types combining skills
- Implement combat system
-
Social Media Features
- Create
Likeable,Commentable,Shareablemixins - Create different post types
- Track engagement
- Create
-
Vehicle Types
- Create
LandVehicle,WaterVehicle,AirVehicle - Create amphibious and flying cars
- Handle mode switching
- Create
-
Employee Benefits
- Create
HealthInsurance,Retirement,StockOptionsmixins - Create employee types with different benefits
- Calculate total compensation
- Create
-
Plugin System
- Dynamic mixin application
- Plugin loader
- Conflict resolution
- Dependency management
-
ORM System
- Model base class
- Serialization mixins
- Validation mixins
- Query capabilities
-
Game Engine Components
- Entity component system
- Renderable, Collidable, Scriptable mixins
- Performance optimization
-
Authentication System
- OAuth, SAML, JWT mixins
- Multi-factor authentication
- Session management
-
Content Delivery
- Cacheable, Compressible, Encryptable mixins
- Different content types
- Delivery optimization
❌ Wrong:
class A:
def method(self):
return "A"
class B:
def method(self):
return "B"
class C(A, B): # Which method wins?
pass
c = C()
print(c.method()) # "A" (first parent wins, but confusing!)✅ Correct:
class C(A, B):
def method(self):
# Explicitly define behavior
return f"C uses: {super().method()}"❌ Wrong:
class A:
def __init__(self):
print("A")
class B(A):
def __init__(self):
A.__init__(self) # Direct call
class C(A):
def __init__(self):
A.__init__(self) # Direct call
class D(B, C):
def __init__(self):
B.__init__(self)
C.__init__(self) # A.__init__ called TWICE!✅ Correct:
class A:
def __init__(self):
print("A")
class B(A):
def __init__(self):
super().__init__() # Use super()
class C(A):
def __init__(self):
super().__init__() # Use super()
class D(B, C):
def __init__(self):
super().__init__() # Calls each parent once❌ Wrong:
class ListMixin:
items = [] # Shared by ALL instances!
def add_item(self, item):
self.items.append(item)✅ Correct:
class ListMixin:
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.items = [] # Each instance gets own list❌ Wrong:
# Too complex!
class SuperUser(User, Admin, Moderator,
PowerUser, PremiumUser, BetaTester):
pass✅ Correct:
# Use composition or simplify
class User:
def __init__(self):
self.permissions = PermissionSet()
self.roles = RoleManager()- Model mixins for timestamps
- Serialization mixins
- Permission mixins
- Audit trail mixins
- Component mixins (Clickable, Draggable, Resizable)
- Event handling
- Style mixins
- Layout mixins
- Component-based entities
- Ability mixins
- Status effect mixins
- AI behavior mixins
- View mixins
- Authentication mixins
- CRUD operation mixins
- API serialization mixins
- Dynamic plugin loading
- Feature mixins
- Dependency resolution
- Version compatibility
- Ability tree system
- Equipment slots
- Status effects
- Skill combinations
- Content types
- Permission mixins
- Workflow states
- Publishing pipeline
- Protocol mixins (WiFi, Bluetooth, Zigbee)
- Sensor capabilities
- Actuator controls
- Power management
- Serialization formats
- Authentication methods
- Rate limiting
- Caching strategies