Python Method Resolution Order (MRO)

Published Sept. 7, 2023, 12:12 p.m.

  1. šŸš€Ā Conceptual Understanding of MRO (Method Resolution Order):

    • šŸ’” Why MRO matters in Python.
    • 🧩 Introduction to the C3 algorithm.
    • šŸ” The DLR (Depth-First Left-to-Right) approach.
    • šŸ„‡ Priority rules: Child > Parent, Left > Right.
    • šŸ‘‘ Head Element vs. Tail Terminology.
  2. 🌐 Finding Merge in MRO:

    • šŸ”„ Exploring the "Merge" process in C3.
    • šŸ› ļø Step-by-step guide for determining MRO.
    • 🧩 Example demonstrating merge for class inheritance.
  3. 🧐 Using mro() Function:

    • šŸ’» Utilizing theĀ mro()Ā function to discover MRO.
  4. šŸ’”Ā Detailed Code Examples:

    • šŸ“š Three code examples illustrating complex class hierarchies.
  5. 🤯 Problem 1: Multiple Inheritance with Conflicting Method Names:

    • ā“ Confronting a problem with conflicting method names.
    • 🧩 Solving it step by step: The MRO journey.
  6. šŸ’ŽĀ Problem 2: Multiple Inheritance with Diamond Shape:

    • šŸ’Ž Tackling a diamond-shaped class hierarchy problem.
    • šŸ“œ Step-by-step resolution using the C3 algorithm.
  7. šŸŽ‰Ā Comprehensive Example:

    • šŸš€ A thrilling example combining all MRO elements.
    • šŸ“¢ Interactive exercise with real code and expected outcomes.

🧠 Conceptual Understanding:

1. MRO Algorithm (C3 Algorithm):

  • šŸ’”Ā Method Resolution Order (MRO)Ā is an algorithm used in Python to determine the sequence in which methods or attributes are resolved in complex class hierarchies.
  • šŸŒ€ The C3 algorithm is the specific MRO algorithm used in Python.
  • šŸš€ It follows aĀ Depth-First Left-to-Right (DLR)Ā approach, meaning it searches from left to right in the inheritance tree.
  • šŸŽÆ Child classes take precedence over parent classes, and left parent classes take precedence over right parent classes.

2. Priority Rules:

  • šŸ„‡ Child classes are given more priority than parent classes when resolving methods or attributes.
  • šŸŽ– Among parent classes, the leftmost parent class is given more priority than the rightmost parent class.

3. Head Element vs. Tail Terminology:

  • šŸŽ© In a list of classes (e.g., C1, C2, C3...), the first class (C1) is considered theĀ Head Element, and the rest are considered theĀ Tail.

4. Finding Merge:

  • šŸ•µļø To determine the MRO for a class, the C3 algorithm uses a process called "Merge."
  • šŸŒŖļø It starts with the head of the first list.
  • 🧩 If the head is not in the tail part of any other list, it adds this head to the result and removes it from the lists in the merge.
  • šŸ”„ If the head is present in the tail part of any other list, it considers the head element of the next list and continues the process.

5. UsingĀ mro()Ā Function:

  • 🧐 You can find the MRO of any class by using theĀ mro()Ā function. For example:Ā print(ClassName.mro()).

Now, let's explore these concepts with detailed code examples.

🧐 Detailed Code Examples:

Example 1:

class A: pass
class B(A): pass
class C(A): pass
class D(B, C): pass

print(A.mro())
print(B.mro())
print(C.mro())
print(D.mro())

Output:

  • ForĀ A:Ā [<class 'A'>, <class 'object'>]
  • ForĀ B:Ā [<class 'B'>, <class 'A'>, <class 'object'>]
  • ForĀ C:Ā [<class 'C'>, <class 'A'>, <class 'object'>]
  • ForĀ D:Ā [<class 'D'>, <class 'B'>, <class 'C'>, <class 'A'>, <class 'object'>]

Example 2:

class A: pass
class B: pass
class C: pass
class X(A, B): pass
class Y(B, C): pass
class P(X, Y, C): pass

print(A.mro())
print(X.mro())
print(Y.mro())
print(P.mro())

Output:

  • ForĀ A:Ā [<class 'A'>, <class 'object'>]
  • ForĀ X:Ā [<class 'X'>, <class 'A'>, <class 'B'>, <class 'object'>]
  • ForĀ Y:Ā [<class 'Y'>, <class 'B'>, <class 'C'>, <class 'object'>]
  • ForĀ P:Ā [<class 'P'>, <class 'X'>, <class 'A'>, <class 'Y'>, <class 'B'>, <class 'C'>, <class 'object'>]

Example 3:

class D: pass
class E: pass
class F: pass
class B(D, E): pass
class C(D, F): pass
class A(B, C): pass

print(D.mro())
print(B.mro())
print(C.mro())
print(A.mro())

Output:

  • ForĀ D:Ā [<class 'D'>, <class 'object'>]
  • ForĀ B:Ā [<class 'B'>, <class 'D'>, <class 'E'>, <class 'object'>]
  • ForĀ C:Ā [<class 'C'>, <class 'D'>, <class 'F'>, <class 'object'>]
  • ForĀ A:Ā [<class 'A'>, <class 'B'>, <class 'C'>, <class 'D'>, <class 'E'>, <class 'F'>, <class 'object'>]

These examples demonstrate the MRO determined by the C3 algorithm in Python, showcasing the sequence in which methods or attributes are resolved within complex class hierarchies. MRO is like a puzzle solver for Python's interpreter, ensuring that it finds the right methods and attributes in a consistent and predictable order. šŸ§©šŸšŸ”

Let's explore two more problems related to Method Resolution Order (MRO) in Python, along with step-by-step solutions.

🧩 Problem 1: Multiple Inheritance with Diamond Shape

Problem:Ā Consider the following class hierarchy with a diamond shape:

class A:
    def foo(self):
        print("A's foo")

class B(A):
    def foo(self):
        print("B's foo")

class C(A):
    def foo(self):
        print("C's foo")

class D(B, C):
    pass

We want to find the MRO for classĀ DĀ and understand how Python resolves the methodĀ foo()Ā in this complex hierarchy.

Solution - Step by Step:

Step 1: Visualize the Hierarchy

Visualize the class hierarchy with a diamond shape:

  A
 / \
B   C
 \ /
  D

Step 2: Define the Rules

Recap the MRO rules:

  • Child classes have higher priority than parent classes.
  • Left parent classes have higher priority than right parent classes.
  • We'll use the C3 algorithm to find the MRO.

Step 3: Apply the C3 Algorithm

Now, let's apply the C3 algorithm to find the MRO for classĀ D.

Determine the MRO for Class A (A.mro()):

  • AĀ has no parent, so its MRO isĀ [A, object].

Determine the MRO for Class B (B.mro()):

  • BĀ inherits fromĀ A, so its MRO isĀ [B, A, object].

Determine the MRO for Class C (C.mro()):

  • CĀ inherits fromĀ A, so its MRO isĀ [C, A, object].

Determine the MRO for Class D (D.mro()):

  • DĀ inherits from bothĀ BĀ andĀ C. Apply the C3 algorithm.
  • Start with the head element (D) and merge the MROs of its parents (BĀ andĀ C) using the C

3 rules:

  • Merge(B.mro(), C.mro()):Ā [B, A, object]Ā andĀ [C, A, object].

  • Merge(B.mro(), C.mro()) becomesĀ [B, C, A, object].

  • Now, add the head element (D) to the merged result:

    • [D]Ā +Ā [B, C, A, object]Ā =Ā [D, B, C, A, object].

Step 4: Final MRO for Class D

The final Method Resolution Order (MRO) for classĀ DĀ isĀ [D, B, C, A, object].

This MRO defines the order in which Python will search for methods or attributes in classĀ D. When callingĀ foo()Ā on an instance ofĀ D, Python will use the method defined in classĀ DĀ itself (B's foo), as it takes precedence over its parent classes.

🧩 Problem 2: Multiple Inheritance with Conflicting Method Names

Problem:Ā Consider the following class hierarchy with multiple inheritance:

class A:
    def foo(self):
        print("A's foo")

class B(A):
    def bar(self):
        print("B's bar")

class C:
    def foo(self):
        print("C's foo")

class D(B, C):
    pass

We want to find the MRO for classĀ DĀ and understand how Python resolves the conflicting method namesĀ foo()Ā in this hierarchy.

Solution - Step by Step:

Step 1: Visualize the Hierarchy

Visualize the class hierarchy with multiple inheritance:

  A
  |
  B
 / \
|   C
|   |
D   |
 \ /

Step 2: Define the Rules

Recap the MRO rules:

  • Child classes have higher priority than parent classes.
  • Left parent classes have higher priority than right parent classes.
  • We'll use the C3 algorithm to find the MRO.

Step 3: Apply the C3 Algorithm

Now, let's apply the C3 algorithm to find the MRO for classĀ D.

Determine the MRO for Class A (A.mro()):

  • AĀ has no parent, so its MRO isĀ [A, object].

Determine the MRO for Class B (B.mro()):

  • BĀ inherits fromĀ A, so its MRO isĀ [B, A, object].

Determine the MRO for Class C (C.mro()):

  • CĀ has no parent, so its MRO isĀ [C, object].

Determine the MRO for Class D (D.mro()):

  • DĀ inherits from bothĀ BĀ andĀ C. Apply the C3 algorithm.

  • Start with the head element (D) and merge the MROs of its parents (BĀ andĀ C) using the C3 rules:

    • Merge(B.mro(), C.mro()):Ā [B, A, object]Ā andĀ [C, object].

    • Merge(B.mro(), C.mro()) becomesĀ [B, C, A, object].

  • Now, add the head element (D) to the merged result:

    • [D]Ā +Ā [B, C, A, object]Ā =Ā [D, B, C, A, object].

Step 4: Final MRO for Class D

The final Method Resolution Order (MRO) for classĀ DĀ isĀ [D, B, C, A, object].

This MRO defines the order in which Python will search for methods or attributes in classĀ D. When callingĀ foo()Ā on an instance ofĀ D, Python will use the method defined in classĀ DĀ itself (A's foo), as it takes precedence over its parent classes.

Here's an example that combines all the features of Method Resolution Order (MRO) in Python, including multiple inheritance, priority rules, head element vs. tail terminology, finding merge using the C3 algorithm, and using theĀ mro()Ā function:

class A:
    def foo(self):
        print("A's foo")

class B(A):
    def bar(self):
        print("B's bar")

class C:
    def foo(self):
        print("C's foo")

class D(B, C):
    pass

class E(C, A):
    def bar(self):
        print("E's bar")

class F(D, E):
    def baz(self):
        print("F's baz")

# Using mro() to find the MRO for class F
print(F.mro())

# Creating an instance of class F
obj_f = F()

# Calling methods
obj_f.foo()  # Calls C's foo (left parent class of E)
obj_f.bar()  # Calls B's bar (left parent class of D)
obj_f.baz()  # Calls F's baz

Output ofĀ F.mro():

[<class '__main__.F'>, <class '__main__.D'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]

In this example:

  1. We have a class hierarchy with multiple inheritance, including classes A, B, C, D, E, and F.
  2. Class F inherits from D and E, and E inherits from C and A.
  3. We use theĀ mro()Ā function to find the Method Resolution Order for class F, which shows the order in which methods will be resolved.
  4. We create an instance of class F and call various methods, demonstrating how Python resolves methods following the MRO defined by the C3 algorithm.

This example showcases the comprehensive use of MRO features in Python's class hierarchy, including the resolution of methods with different priorities and the use of theĀ mro()Ā function to visualize the order.