Python
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax and Data Types
-
Section 3: Collections
-
39: Set Operations: Union, Intersection, Difference
-
Section 4: Control Flow
-
Section 5: Functions
-
Section 6: Turtle Graphics and Early Practice Projects
-
Section 7: Working with Files and I/O
-
Section 8: Regular Expressions
-
Section 9: Object-Oriented Python
-
Section 10: Error Handling
-
Section 11: Modules and Packages
-
Section 12: Iterators, Generators, and Functional Tools
-
Section 13: Decorators and Metaprogramming
-
Section 14: Concurrency and Parallelism
-
Section 15: Working with Dates, Times, and Numbers
-
Section 16: Standard Library Deep Dive I: Data Structures
-
Section 17: Standard Library Deep Dive II: System and Introspection
-
Section 18: Standard Library Deep Dive III: Security and Encoding
-
Section 19: Standard Library Deep Dive IV: Text and Data Utilities
-
Section 20: Networking and Web Basics
-
Section 21: Working with Databases
-
Section 22: Testing and Quality
-
Section 23: Advanced Typing
-
Section 24: Context Managers and Resource Handling
-
Section 25: Text, Unicode, and Binary Data
-
Section 26: More Functional and Iteration Tools
-
Section 27: Data Validation and Configuration
-
Section 28: Working with Images and Media
-
Section 29: Property-Based and Documentation Testing
-
Section 30: Packaging and Deployment
-
Section 31: Performance and Internals
-
Section 32: Design Patterns in Python
-
Section 33: GUI Programming
-
Section 34: Security Basics
-
Section 35: Data Structures and Algorithms
-
Section 36: Practical Projects
-
Section 37: Capstone Projects
-
Section 38: Interview and Algorithm Practice
-
Section 39: Writing Idiomatic Python
120: Multiple Inheritance and the MRO
Multiple inheritance is one of those Python features that looks powerful on paper but can quickly turn into a nightmare if you don't understand how Python decides which method to actually run. We're talking about the Method Resolution Order, or MRO. To make sense of this, let's build a character system for a tabletop RPG.
Defining the combatant hierarchy
I want a base class for any entity that can fight, and then specialized classes for different roles. Let's start simple. Every Combatant has a name, and then we'll have Warrior and Mage subclasses that add their own specific stats.
class Combatant:
def __init__(self, name):
print("Initializing Combatant")
self.name = name
class Warrior(Combatant):
def __init__(self, name, strength):
print("Initializing Warrior")
Combatant.__init__(self, name)
self.strength = strength
class Mage(Combatant):
def __init__(self, name, intelligence):
print("Initializing Mage")
Combatant.__init__(self, name)
self.intelligence = intelligence
At this point, everything feels normal. I'm explicitly calling the parent's __init__ method. It's clear, it's direct, and it works for single inheritance. But here is where I'm about to walk into a trap.
The hybrid Spellblade and the double-init bug
Now, I want to create a Spellblade—a character who is both a Warrior and a Mage. Since Python allows multiple inheritance, I'll just list both as parents.
class Spellblade(Warrior, Mage):
def __init__(self, name, strength, intelligence, weapon):
print("Initializing Spellblade")
Warrior.__init__(self, name, strength)
Mage.__init__(self, name, intelligence)
self.weapon = weapon
# Let's see what happens
sb = Spellblade("Valerius", 15, 15, "Runeblade")
If you run this, the output is:
- Initializing Spellblade
- Initializing Warrior
- Initializing Combatant
- Initializing Mage
- Initializing Combatant
Wait. Why did Initializing Combatant print twice? This is a classic mistake. Because I called Warrior.__init__ and Mage.__init__ explicitly, and both of those called Combatant.__init__, the base class was initialized twice. In a real app, this could mean resetting a database connection twice or clearing a list you just populated. It's inefficient and dangerous.
Fixing the flow with super()
To fix this, we need to stop calling parent classes by name and start using super(). A common misconception is that super() just calls the "parent." In reality, it calls the next class in the MRO. For super() to work in a multiple inheritance scenario, every class in the chain must use it.
class Combatant:
def __init__(self, name, **kwargs):
print("Initializing Combatant")
self.name = name
class Warrior(Combatant):
def __init__(self, strength, **kwargs):
print("Initializing Warrior")
super().__init__(**kwargs)
self.strength = strength
class Mage(Combatant):
def __init__(self, intelligence, **kwargs):
print("Initializing Mage")
super().__init__(**kwargs)
self.intelligence = intelligence
class Spellblade(Warrior, Mage):
def __init__(self, name, strength, intelligence, weapon):
print("Initializing Spellblade")
# We pass the specific args for the next classes in the MRO
super().__init__(name=name, strength=strength, intelligence=intelligence)
self.weapon = weapon
I've added **kwargs here because super() doesn't know which parent is next in line. By using keyword arguments, Warrior can pluck out the strength it needs and pass the rest (like intelligence and name) up the chain until they reach the class that actually needs them.
Visualizing the MRO
If you're wondering how Python knows that Warrior comes before Mage, it's using an algorithm called C3 Linearization. You don't need to memorize the math, but you should know how to inspect the result. Every Python class has an __mro__ attribute.
print(Spellblade.__mro__)
# Output: (, , , , )
This is the "search list." When you call a method, Python looks in Spellblade. Not there? It checks Warrior. Not there? Mage. Then Combatant. Finally, it hits object (the root of all Python classes). This linear path is what prevents the "diamond problem" where a base class might be visited multiple times.
📋 Practical Task
Building a Technomancer Class Hierarchy
You need to implement a character system for a Sci-Fi RPG. Create the following class structure ensuring that the base class is only initialized once:
- A base class
Entitythat initializes aname. - A class
Hackerthat inherits fromEntityand initializescoding_skill. - A class
Psionicthat inherits fromEntityand initializesmind_power. - A hybrid class
Technomancerthat inherits from bothHackerandPsionic, and initializes adevice.
Requirements:
- Use
super()in all__init__methods. - Use
**kwargsto ensure arguments are passed correctly through the MRO. - Print a unique message in each
__init__(e.g., "Init Hacker") so you can verify the execution order. - Instantiate a
Technomancerand print its__mro__to the console.
There are no comments for now.