Advanced Class Mechanics

Difficulty: Advanced

Question

How does super() work in Python with multiple inheritance? What are mixins?

Answer

Python uses Method Resolution Order (MRO) - a linear ordering of base classes computed by the C3 linearization algorithm.

super() follows the MRO - it calls the next class in the MRO, not necessarily the direct parent. This is crucial for cooperative multiple inheritance.

Mixins: small, focused classes that add behavior to other classes without being used standalone. They should not define __init__ (or call super().__init__() cooperatively).

__init_subclass__: called whenever a class is subclassed, enabling plugin registries and validation.

Code examples

MRO and Cooperative super()

class A:
    def method(self):
        print('A.method')
        super().method()  # Cooperative - passes to next in MRO

class B(A):
    def method(self):
        print('B.method')
        super().method()

class C(A):
    def method(self):
        print('C.method')
        super().method()

class D(B, C):
    def method(self):
        print('D.method')
        super().method()

print(D.__mro__)  # D -> B -> C -> A -> object
d = D()
d.method()
# D.method -> B.method -> C.method -> A.method

# super() follows MRO, not just the parent
# B.super() goes to C (next in D's MRO), not A!

# Verify MRO
for cls in D.__mro__:
    print(cls.__name__)

super() in B calls C, not A - it follows the MRO of the instance (D), not B's own parent. This is cooperative multiple inheritance.

Mixins and __init_subclass__

# Mixin: no __init__, no standalone use
class LoggedMixin:
    def __getattribute__(self, name):
        result = super().__getattribute__(name)
        if callable(result):
            print(f'Accessing: {name}')
        return result

class JSONMixin:
    def to_json(self):
        import json
        return json.dumps(vars(self))

    @classmethod
    def from_json(cls, data):
        import json
        return cls(json.loads(data))

# Compose with multiple inheritance
class User(JSONMixin, LoggedMixin):
    def __init__(self, name, email):
        self.name = name
        self.email = email

u = User('Alice', 'alice@example.com')
print(u.to_json())

# __init_subclass__: plugin registry
class BasePlugin:
    _plugins = {}

    def __init_subclass__(cls, plugin_name=None, kwargs):
        super().__init_subclass__(kwargs)
        if plugin_name:
            BasePlugin._plugins[plugin_name] = cls

class EmailPlugin(BasePlugin, plugin_name='email'):
    def send(self): print('Sending email')

class SMSPlugin(BasePlugin, plugin_name='sms'):
    def send(self): print('Sending SMS')

print(BasePlugin._plugins)
# {'email': EmailPlugin, 'sms': SMSPlugin}

__init_subclass__ runs when any subclass is defined - enables automatic plugin registration without explicit registration calls.

Key points

Concepts covered

__init_subclass__, ABC, mixin, multiple inheritance, super()