Keywords: Python | super() | multiple inheritance | method resolution order | MRO | cooperative inheritance
Abstract: This technical article provides a comprehensive analysis of Python's super() function in multiple inheritance scenarios, focusing on the C3 linearization algorithm for Method Resolution Order (MRO). Through detailed code examples, it demonstrates how super() traverses the inheritance hierarchy, explains cooperative inheritance patterns, parameter passing strategies, and common pitfalls. The article combines official documentation with community insights to offer a complete guide for effective multiple inheritance design in Python.
Fundamentals of super() and Multiple Inheritance
In Python object-oriented programming, the super() function is a crucial mechanism for method invocation, particularly in multiple inheritance scenarios. When a class inherits from multiple parent classes, Python employs the Method Resolution Order (MRO) to determine attribute lookup priority.
Consider this basic example:
class First(object):
def __init__(self):
print("first")
class Second(object):
def __init__(self):
print("second")
class Third(First, Second):
def __init__(self):
super(Third, self).__init__()
print("that's it")
When instantiating the Third class, super(Third, self).__init__() will call the First.__init__ method. This occurs because Python's MRO algorithm traverses the inheritance chain from left to right, prioritizing First as the first parent class.
C3 Linearization for Method Resolution Order
Python uses the C3 linearization algorithm to compute MRO, ensuring:
- Subclasses precede parent classes
- Multiple parents maintain declaration order
- Monotonicity (consistency in inheritance relationships)
The specific resolution order can be inspected via the Class.__mro__ attribute:
>>> Third.__mro__
(<class '__main__.Third'>, <class '__main__.First'>,
<class '__main__.Second'>, <type 'object'>)
Cooperative Inheritance and super() Call Chains
Proper multiple inheritance design requires all relevant classes to use super() forming cooperative call chains:
class First(object):
def __init__(self):
super(First, self).__init__()
print("first")
class Second(object):
def __init__(self):
super(Second, self).__init__()
print("second")
class Third(First, Second):
def __init__(self):
super(Third, self).__init__()
print("third")
Instantiating Third() produces the output sequence:
second
first
third
Call flow analysis:
Third.__init__callssuper(), MRO points toFirst.__init__First.__init__callssuper(), MRO points toSecond.__init__Second.__init__callssuper(), MRO points toobject.__init__- The call stack returns sequentially to each
__init__method, executing print statements
MRO Calculation in Complex Inheritance Structures
For complex scenarios like diamond inheritance, MRO calculation follows specific rules. Consider this hierarchy:
class First(object):
def __init__(self):
print("first")
class Second(First):
def __init__(self):
print("second")
class Third(First):
def __init__(self):
print("third")
class Fourth(Second, Third):
def __init__(self):
super(Fourth, self).__init__()
print("that's it")
This structure's MRO is: [Fourth, Second, Third, First, object]. Python's C3 algorithm ensures each class appears only once in the MRO while maintaining logical inheritance order.
MRO Conflicts and Error Handling
When inheritance relationships cannot form a consistent MRO, Python raises an exception:
class First(object):
def __init__(self):
print("first")
class Second(First):
def __init__(self):
print("second")
class Third(First, Second):
def __init__(self):
print("third")
This definition causes:
TypeError: Error when calling the metaclass bases
Cannot create a consistent method resolution order (MRO) for bases Second, First
Conflict reason: Third inherits from both First and Second, while Second inherits from First, creating contradictory inheritance order requirements.
Parameter Passing and Cooperative Method Design
Method parameter passing in multiple inheritance requires special design. Reference this improved pattern:
class Car:
def __init__(self, *, door, wheel, **kwargs):
super().__init__(**kwargs)
self.door = door
self.wheel = wheel
def start(self):
print('Start the Car')
class Flyable:
def __init__(self, *, wing, **kwargs):
super().__init__(**kwargs)
self.wing = wing
def start(self):
print('Start the Flyable object')
class FlyingCar(Flyable, Car):
pass
# Instantiation requires all necessary parameters
car = FlyingCar(wing=1, wheel=2, door=3)
car.start() # Output: Start the Flyable object
Key design points:
- Use
**kwargsto receive and forward unknown parameters - Keyword arguments ensure explicit parameter passing
- All classes uniformly use
super()calls FlyingCarrelies on MRO to automatically select the correctstartmethod
Advanced Application: Custom Base Classes and Parameter Management
For complex multiple inheritance systems, specialized base classes can manage super() calls:
class SuperObject:
def __init__(self, **kwargs):
mro = type(self).__mro__
# Validate MRO structure
assert mro[-1] is object
if mro[-2] is not SuperObject:
raise TypeError('Inheritance hierarchy error')
super().__init__()
def super_call(self, super_, funcname, **kwargs):
"""Cooperative method invocation utility"""
super_func = getattr(super_, funcname, None)
if super_func is not None:
return super_func(**kwargs)
class A(SuperObject):
def __init__(self, **kwargs):
print("A")
super(A, self).__init__(**kwargs)
class B(SuperObject):
def __init__(self, **kwargs):
print("B")
super(B, self).__init__(**kwargs)
class C(A, B):
def __init__(self, **kwargs):
print("C")
super(C, self).__init__(**kwargs)
This design pattern ensures:
- Unified
super()calling convention - Safe parameter passing mechanism
- Extensible method cooperation framework
Best Practices Summary
Based on Python community experience, using super() in multiple inheritance should follow:
- Consistency Principle: All classes in the inheritance hierarchy should either use
super()or none should - Parameter Design: Methods should use
**kwargsto receive additional parameters, ensuring parameter chain畅通 - MRO Verification: Use
Class.__mro__to verify resolution order before complex inheritance structures - Avoid Diamond Inheritance: Minimize complex multiple inheritance, prefer composition or mixin patterns
- Documentation: Public inheritance hierarchies should clearly document expected MRO behavior
By understanding MRO mechanisms and correctly using super(), developers can build robust multiple inheritance systems, fully leveraging Python's powerful object-oriented programming capabilities.