Inheritance - Types, Method Resolution Order & Diamond Problem - Watch the Algorithm Execute, Step by Step
Watching the step-by-step construction and method lookup in the diamond inheritance pattern reveals the subtlety of MRO and why it matters to avoid ambiguity.
Step 1/10
·Active fill★Answer cell
Defines base class with a concrete method.
A
+method()
Subclass B overrides method from A.
A
+method()
B
+method()
B ▷ A (1:1)
Subclass C overrides method from A.
A
+method()
B
+method()
C
+method()
B ▷ A (1:1)C ▷ A (1:1)
Multiple inheritance creates diamond shape.
A
+method()
B
+method()
C
+method()
D
B ▷ A (1:1)C ▷ A (1:1)D ▷ B (1:1)D ▷ C (1:1)
MRO computed using C3 linearization to resolve diamond problem.
A
+method()
B
+method()
C
+method()
D
B ▷ A (1:1)C ▷ A (1:1)D ▷ B (1:1)D ▷ C (1:1)
Instance created and method call initiated.
A
+method()
B
+method()
C
+method()
D
−instance: D
B ▷ A (1:1)C ▷ A (1:1)D ▷ B (1:1)D ▷ C (1:1)
Method lookup moves to B after D lacks method.
A
+method()
B
+method()
C
+method()
D
−instance: D
B ▷ A (1:1)C ▷ A (1:1)D ▷ B (1:1)D ▷ C (1:1)
Method resolved to B's implementation.
A
+method()
B
+method()
C
+method()
D
−instance: D
B ▷ A (1:1)C ▷ A (1:1)D ▷ B (1:1)D ▷ C (1:1)
Method execution confirms MRO correctness.
A
+method()
B
+method()
C
+method()
D
−instance: D
B ▷ A (1:1)C ▷ A (1:1)D ▷ B (1:1)D ▷ C (1:1)
MRO resolves diamond inheritance ambiguity.
A
+method()
B
+method()
C
+method()
D
B ▷ A (1:1)C ▷ A (1:1)D ▷ B (1:1)D ▷ C (1:1)
Key Takeaways
✓ Method Resolution Order (MRO) linearizes multiple inheritance hierarchies to avoid ambiguity.
Reading code alone rarely clarifies how MRO works internally; visualization shows the exact search order.
✓ The diamond inheritance problem arises when two subclasses inherit from the same base and a subclass inherits both, causing potential method conflicts.
Seeing the diamond shape and method lookup path visually helps understand why this is a problem.
✓ Python's C3 linearization algorithm ensures a consistent and predictable method lookup order in complex inheritance graphs.
The step-by-step MRO computation and method call resolution demonstrate this algorithm concretely.
Practice
(1/5)
1. When a class implements an interface and also extends an abstract class, what is the sequence of abstraction enforcement and implementation that occurs during object instantiation?
easy
A. The abstract class constructor runs, but interface methods have no implementation to run.
B. Interface methods are implemented first, then the abstract class constructor runs.
C. The class must implement interface methods before the abstract class constructor runs.
D. The abstract class's constructor runs first, then interface methods are implemented by the class.
Solution
Step 1: Understand interface role
Interfaces declare methods but provide no implementation or constructors.
Step 2: Abstract class constructor behavior
Abstract class constructors run during instantiation to initialize shared state.
Step 3: Implementation of interface methods
The class implementing the interface provides method bodies; no constructor or code runs from interface itself.
Final Answer:
Option A -> Option A
Quick Check:
Interfaces define contracts only; abstract class constructors run during instantiation.
Hint: Interface methods have no constructor or implementation to run; abstract class constructors always run.
Common Mistakes:
Thinking interface methods have constructors or code to execute.
Believing interface implementation order affects constructor execution.
Confusing interface method implementation with constructor invocation.
2. In the object-oriented design of a Snake and Ladder game, which component is primarily responsible for managing the state transitions of a player's position after a dice roll?
easy
A. The Dice class, since it generates the number that determines movement
B. The Player class, as it holds the current position and updates it directly
C. The Board class, because it contains the snakes and ladders and applies their effects
D. The GameController class, which orchestrates the game flow and updates player positions accordingly
Solution
Step 1: Understand the role of Dice
The Dice only generates a random number; it does not manage state transitions.
Step 2: Consider Player class responsibilities
Player holds position but should not decide how to update it considering snakes or ladders.
Step 3: Analyze Board class role
Board knows snakes and ladders but does not manage player state transitions directly.
Step 4: Role of GameController
GameController coordinates dice roll, queries Board for snakes/ladders, and updates Player position accordingly.
Final Answer:
Option D -> Option D
Quick Check:
GameController centralizes state transitions, ensuring separation of concerns.
Hint: GameController orchestrates state changes, not Dice or Player alone [OK]
Common Mistakes:
Thinking Dice manages player position
Assuming Player updates position without Board's input
Believing Board directly changes player state
3. Trace the sequence of events when a client calls a method on a subclass instance that violates the Liskov Substitution Principle by strengthening a postcondition. What happens step-by-step?
easy
A. The client receives a result that meets the superclass contract, so no issues arise.
B. The subclass method returns a stricter result than expected, potentially causing client failures.
C. The client silently ignores the stricter postcondition, so behavior is unaffected.
D. The subclass method throws an exception due to the strengthened postcondition.
Solution
Step 1: Recall LSP postcondition rule
Subclasses must not strengthen postconditions; they can only maintain or weaken them.
Step 2: Trace client call
The client expects results conforming to the superclass contract. If subclass returns stricter results, some clients expecting broader results may fail.
Step 3: Analyze options
The subclass method returns a stricter result than expected, potentially causing client failures. correctly identifies potential client failures due to stricter postconditions. The client receives a result that meets the superclass contract, so no issues arise. is false because stricter postconditions can break clients. The client silently ignores the stricter postcondition, so behavior is unaffected. is incorrect; clients cannot ignore contract violations silently. The subclass method throws an exception due to the strengthened postcondition. is not guaranteed; exceptions are not implied by postcondition strengthening.
Hint: Strengthening postconditions breaks client assumptions and causes failures.
Common Mistakes:
Assuming stricter postconditions are safe
Believing clients ignore contract violations
Confusing exceptions with contract violations
4. Given the following thread-safe observer pattern code snippet, what will be the output after the sequence of operations shown below?
import threading
class Observer:
def __init__(self, name):
self.name = name
def update(self, event_type, message):
print(f"{self.name} received {event_type}: {message}")
class Subject:
def __init__(self):
self.lock = threading.Lock()
self.observers = {}
def subscribe(self, observer, event_type):
with self.lock:
if event_type not in self.observers:
self.observers[event_type] = set()
self.observers[event_type].add(observer)
def unsubscribe(self, observer, event_type):
with self.lock:
if event_type in self.observers and observer in self.observers[event_type]:
self.observers[event_type].remove(observer)
if not self.observers[event_type]:
del self.observers[event_type]
def notify(self, event_type, message):
with self.lock:
observers_snapshot = list(self.observers.get(event_type, []))
for observer in observers_snapshot:
observer.update(event_type, message)
subject = Subject()
obs1 = Observer('Obs1')
obs2 = Observer('Obs2')
subject.subscribe(obs1, 'eventA')
subject.subscribe(obs2, 'eventB')
subject.notify('eventA', 'Hello A')
subject.notify('eventB', 'Hello B')
subject.unsubscribe(obs1, 'eventA')
subject.notify('eventA', 'Hello again A')
What is printed?
easy
A. Obs1 received eventA: Hello A
Obs2 received eventB: Hello B
Obs1 received eventA: Hello again A
B. Obs1 received eventA: Hello A
Obs1 received eventA: Hello again A
Obs2 received eventB: Hello B
C. Obs2 received eventB: Hello B
Obs1 received eventA: Hello again A
D. Obs1 received eventA: Hello A
Obs2 received eventB: Hello B
Solution
Step 1: Trace subscriptions and notifications
Obs1 subscribes to 'eventA', Obs2 to 'eventB'. First notify('eventA') calls Obs1.update, printing "Obs1 received eventA: Hello A". Then notify('eventB') calls Obs2.update, printing "Obs2 received eventB: Hello B".
Step 2: Trace unsubscribe and final notification
Obs1 unsubscribes from 'eventA'. The final notify('eventA') finds no observers, so no output for Obs1. Therefore, the last notification does not print anything.
Final Answer:
Option D -> Option D
Quick Check:
Unsubscribed observers do not receive notifications [OK]
Hint: Unsubscribed observers do not receive notifications [OK]
Common Mistakes:
Assuming unsubscribed observers still get notified
5. Identify the bug in the following Python code implementing deep copy for the Profile class:
import copy
class Profile:
def __init__(self, name, scores):
self.name = name
self.scores = scores
def __deepcopy__(self, memo):
new_name = self.name # Bug here
new_scores = copy.deepcopy(self.scores, memo)
return Profile(new_name, new_scores)
original = Profile('Alice', [10, 20])
copy_obj = copy.deepcopy(original)
copy_obj.scores.append(30)
print(original.scores)
medium
A. Line assigning new_name = self.name does not deepcopy the name string
B. Line assigning new_scores = copy.deepcopy(self.scores, memo) incorrectly copies scores
C. The __init__ method does not initialize scores properly
D. The return statement returns a new Profile instead of modifying self
Solution
Step 1: Examine __deepcopy__ method
The line new_name = self.name copies the reference to the name string instead of deep copying it.
Step 2: Understand impact
Strings are immutable in Python, so shallow copy is usually safe, but if name were a mutable object, this would cause shared references and bugs. Proper deep copy should be used for consistency.
Final Answer:
Option A -> Option A
Quick Check:
Only new_name assignment lacks deepcopy -> subtle bug [OK]
Hint: All nested fields must be deep copied consistently [OK]
Common Mistakes:
Forgetting to deepcopy all nested fields, assuming immutables are safe
Recommended
Ready to prove it?
Pass the test and this pattern moves unknownstrongwhile earning +1 Grip toward your certificate.