Mixins & Multiple Inheritance
A mixin is a small, focused class that adds exactly one piece of behavior — logging, serialization, comparison helpers — and is never meant to be used on its own. It's designed to be combined with another class through inheritance, contributing its behavior without being a "real" standalone type in the program.
Unlike Java, which restricts a class to extending exactly one superclass, Python supports true multiple inheritance: a class can inherit from several base classes at once by listing them all in the class definition, like class Foo(Base, MixinA, MixinB):. This is precisely what makes the mixin pattern possible in Python.
Method Resolution Order (MRO)
When a class inherits from multiple parents, Python needs a consistent rule for which class's version of a method wins if more than one parent defines it. That rule is the Method Resolution Order (MRO) — the specific sequence of classes Python searches, in order, when looking up an attribute or method. You can inspect it directly on any class with ClassName.__mro__, or read a friendlier version via help(ClassName).
Python computes the MRO using an algorithm called C3 linearization. Without going into its internals, the intuition is enough: it merges each parent's own MRO from left to right, preserving each parent's internal ordering, which guarantees that a parent class always appears before its own ancestors in the final order. The result is deterministic — for a given class hierarchy, the MRO is always the same.
A Practical Mixin: SerializableMixin
Here's a mixin that adds a to_json() method to any class, based only on that instance's own __dict__ (the dictionary Python uses internally to store instance attributes). It doesn't define __init__ or know anything about what it's mixed into — it just assumes the instance has some attributes to serialize.
import json
class SerializableMixin:
def to_json(self):
return json.dumps(self.__dict__)
class User(SerializableMixin):
def __init__(self, username, email):
self.username = username
self.email = email
class Product(SerializableMixin):
def __init__(self, name, price):
self.name = name
self.price = price
user = User("ada", "ada@example.com")
product = Product("Keyboard", 49.99)
print(user.to_json())
print(product.to_json()){"username": "ada", "email": "ada@example.com"}
{"name": "Keyboard", "price": 49.99}User and Product are otherwise unrelated classes — one represents a person, the other a catalog item. Mixing in SerializableMixin gives both of them .to_json() for free, with zero duplicated code between them.
Checking the MRO
print(User.__mro__)
(<class '__main__.User'>, <class '__main__.SerializableMixin'>, <class 'object'>)
This confirms the lookup order: Python checks User first, then SerializableMixin, then falls back to object. If User had defined its own to_json, that version would win, since User comes before the mixin in the MRO.
Mixin Conventions
A mixin adds one specific, focused behavior — not a general-purpose base to build a whole hierarchy on.
A mixin is not meant to be instantiated by itself; it only makes sense combined with another class.
A mixin usually does not define
__init__, so it doesn't interfere with the main class's own constructor.Mixin class names conventionally end in "Mixin" so their purpose is obvious at a glance.