Mixins offer a powerful way to reuse code across multiple Python classes without forcing them into a rigid inheritance hierarchy. Instead of building deep and brittle class trees, you can use mixins to share common behaviors in a modular and flexible way. Learn when and how to implement mixin classes effectively in your Python projects.
By the end of this tutorial, you’ll understand that:
- Mixins allow you to add isolated, reusable functionalities to classes without enforcing a strict type hierarchy.
- Python has no dedicated syntax for declaring mixins. You create mixins by defining classes with specific behaviors that other classes can inherit without forming an is-a relationship.
- Mixins rely on multiple inheritance to combine features from different classes, enhancing flexibility and code reuse.
- Stateful mixins require careful design to manage instance attributes and avoid conflicts with other classes.
- Python’s method resolution order (MRO) determines the order in which classes are inherited, affecting how mixins are applied.
To get the most out of this tutorial, you should have a good understanding of object-oriented programming (OOP), SOLID principles, inheritance, and Python classes.
So, what is a Python mixin, and when should you use one?
Get Your Code: Click here to download the free sample code that shows you how to use mixin classes in Python.
Take the Quiz: Test your knowledge with our interactive “What Are Mixin Classes in Python?” quiz. You’ll receive a score upon completion to help you track your learning progress:
Interactive Quiz
What Are Mixin Classes in Python?Test your knowledge of Python mixins—specialized classes that let you reuse methods without traditional inheritance.
In Short: Mixins Encapsulate Reusable Behaviors
In object-oriented programming (OOP), a mixin is a tool that allows you to reuse a common piece of functionality across several, often unrelated data types while keeping them loosely coupled. In practical terms, mixins can help you achieve a cleaner design with more flexible code consisting of modular and composable pieces.
Many programming languages, including Ruby, Dart, and Scala, support this pattern explicitly through specialized syntax and keywords. Others let you simulate mixins with existing constructs, such as default method implementations in Java interfaces. Conversely, Python relies on multiple inheritance as the underlying mechanism to facilitate the same concept.
Python mixins are ordinary classes with a special meaning. You use them to give other classes new superpowers or to modify their current behavior without forming a rigid type hierarchy. Mixins can also act as a form of dependency injection, allowing you to customize code beyond your control, for example, by plugging into a third-party library or framework.
To give you an idea of when mixin classes are most useful, imagine that you wanted to represent arbitrary Python objects with different data formats. Implementing the same serialization logic in each class would lead to duplicate code, which is a well-known code smell that violates the DRY principle:

Notice the repeated .serialize() method, which appears across all classes in the UML class diagram above. You can fix this issue by extracting the shared functionality into a common base class and creating the following inheritance hierarchy:

But now you end up with an awkward inheritance hierarchy where animals and vehicles share a common ancestor. You’ve grouped them under a shared interface for structural reasons, while they conceptually belong to entirely different categories.
Moreover, a design centered around inheritance can be fragile, making it hard to accommodate future use cases. Maybe you’ll need to implement a mechanical bird equipped with a vehicle’s engine and the ability to fly, but without needing to eat or having a VIN number. As it stands, there’s no straightforward way to fit that kind of data type into your class hierarchy without making inelegant compromises.
Another issue that sometimes arises from using inheritance is a tendency for overgeneralization. When the base class becomes too broad in scope, subclasses start to inherit unnecessary features. For example, if a vehicle requires another serialization method, then all animals automatically inherit it, whether they need one or not. This problem was aptly described by Joe Armstrong, the creator of Erlang, in an interview for Coders at Work:
Because the problem with object-oriented languages is they’ve got all this implicit environment that they carry around with them. You wanted a banana but what you got was a gorilla holding the banana and the entire jungle.
— Joe Armstrong
A better approach would be to design your types based on what they do rather than what they are. This means favoring composition over inheritance and delegating responsibilities to smaller, more focused components, promoting separation of concerns:

Compared to the inheritance-based version, this diagram looks much more intimidating due to the greater number of individual components. On the other hand, such a fine-grained design allows you to compose behaviors in novel ways, which could’ve been difficult to anticipate during the initial requirements analysis.
Wouldn’t it be ideal to combine the convenience of inheritance with the flexibility of composition? That’s precisely where mixins shine! Now that you understand the broader context, it’s time to dig a little deeper by answering more specific questions about mixins.
How Can You Recognize a Mixin Class in the Wild?
For starters, here’s a straightforward example of a mixin class defined in Python:
class SerializableMixin:
def serialize(self) -> dict:
if hasattr(self, "__slots__"):
return {
name: getattr(self, name)
for name in self.__slots__
}
else:
return vars(self)
This class defines one method, which returns a dictionary representation of an object, handling two cases. If the object uses .__slots__, then .serialize() builds a dictionary using a comprehension expression. Otherwise, the method returns an object’s .__dict__ attribute by calling vars() on it.
Note: This sample implementation of object serialization cuts corners by ignoring certain edge cases for brevity. Sometimes, an object may have both a .__dict__ and .__slots__ attributes, for example.
The example above demonstrates some of the key takeaways about Python mixins. In particular, you can recognize a mixin class by the following traits:
- Naming convention: That’s the most obvious giveaway. While not mandatory, it’s considered Pythonic to include a distinctive suffix in the class name. This clearly communicates the special purpose of your class and its intended use since Python has no dedicated syntax for mixins. Additionally, its name should reflect the feature provided by the mixin, like the ability to serialize an object.
- Plain class: Because mixins are meant to provide isolated, reusable features—rather than to enforce an inheritance structure—they typically don’t have any parent classes. This minimizes their scope and reduces the likelihood of incorrect name resolution in multiple inheritance scenarios.
- Single behavior: A mixin class has just one responsibility, making it reusable and composable with other mixins—as long as those mixins provide orthogonal or non-overlapping features. Note that this doesn’t necessarily mean that your mixin must only define a single method.
- Statelessness: Mixins rarely define constructors or instance attributes of their own, making them safe to integrate with other classes through multiple inheritance without conflicts. The behavior encapsulated by a mixin class depends solely on an external state. Mixins often rely on attributes of the classes they’re mixed into.
- Non-standalone: Mixins are designed to augment other classes with new or modified behaviors. They typically don’t make any sense independently, so you almost never instantiate mixin classes directly.
Okay. Now that you’re familiar with this high-level checklist, you can delve into each of these concepts by looking at some hands-on examples.
What Makes a Python Class Qualify as a Mixin?
There’s no syntactical difference between standard classes and mixin classes—the distinction is purely semantic. Because Python provides no dedicated syntax for declaring and using mixins, most developers follow well-known naming conventions to communicate their intended role. Below are two examples of mixins, drawn from the Python standard library and the popular Django web framework:
As you can see, both classes have descriptive names, which include either the MixIn or Mixin suffix. This pattern makes their purpose clear at a glance for anyone reading the code.
More importantly, a mixin class has a single responsibility. It defines and implements exactly one behavior, which other classes can reuse by means of class inheritance. When you include multiple mixins in a class, you compose several orthogonal or independent features in a conflict-free way.
Notice the emphasis on the word “include” in the last sentence, which is often mentioned when referring to mixins. Even though applying Python mixins boils down to using inheritance, extending a mixin doesn’t inherently create a subtyping relation between the parent and the child class. How come?
Inheritance can generally be understood in two ways:
- As a mechanism for code reuse
- As a tool to establish a subtyping relationship
These two uses are also known as implementation inheritance and interface inheritance, respectively. In Python, you typically use inheritance for both purposes at the same time—to reuse members of the base class and to define its more specialized subtype that can replace the parent type where applicable.
In contrast, mixins are primarily focused on reusing code without implying a strong is-a relationship. Conceptually, classes that extend mixins don’t become their subtypes. Instead, they borrow their functionality. Because mixins aren’t intended to be used polymorphically, treating them as interchangeable with their base classes often violates the Liskov substitution principle.
This leads you to another key observation about mixins. Unlike regular classes, which can exist independently, mixin classes typically don’t. As non-standalone entities, mixins aren’t meant to be instantiated. They’re merely a lightweight ingredient designed to be combined—or mixed in—with other classes to enhance their behavior. Creating an instance of a mixin is pointless at best, and may even raise an error in some cases.
If this sounds familiar, then that’s because mixins resemble abstract base classes (ABCs) to a degree. You’ll now compare the two to get a better understanding of their unique roles in object-oriented programming.
How Do Mixins Compare to Abstract Base Classes?
Mixins and abstract base classes share one common characteristic: both serve as building blocks meant to be combined with one or more target classes through inheritance. Consequently, you can’t use them in isolation because they have no purpose on their own.
As an example, consider the mixin class below, which implements JSON serialization of objects with a .__dict__ attribute. Notice that the underlying behavior depends on attributes defined elsewhere—that’s by design:
>>> import json
>>> class JSONSerializableMixin:
... def as_json(self) -> str:
... return json.dumps(vars(self))
...
>>> mixin_instance = JSONSerializableMixin()
>>> mixin_instance.as_json()
'{}'
Although Python doesn’t prevent you from creating instances of such a mixin class directly, the resulting objects aren’t particularly useful by themselves because they lack attributes of their own. As a result, calling mixin_instance.as_json() produces an empty JSON object ({}) encoded as a Python string.
Note: Mixins must either remain stateless or manage their state carefully to reduce the risk of interference with the data attributes of the classes they’re mixed into. So, you’ll rarely find a custom constructor—the .__init__() method—in a mixin class.
To truly benefit from your mixin, you should integrate it into another class with its own attributes, such as this one:
>>> from dataclasses import dataclass
>>> @dataclass
... class User(JSONSerializableMixin):
... user_id: int
... email: str
...
>>> User(555, "jdoe@example.com").as_json()
'{"user_id": 555, "email": "jdoe@example.com"}'
By extending the JSONSerializableMixin, you inherit the .as_json() method, which brings the capability to serialize instances of the User data class into a JSON string.
At first glance, this approach might seem no different than conventional inheritance in Python. However, because mixins don’t constitute standalone data types, the User class in the example above doesn’t become a more specialized subtype of JSONSerializableMixin. Instead, the derived class acquires additional functionality.
A mixin is merely a vessel for delivering one specific behavior that many classes can share without establishing an is-a relationship. In a sense, mixins serve as partial implementations, offering reusable functionalities without the baggage and constraints of interface inheritance.
Note: Although mixin classes implement a single behavior, they may occasionally consist of more than one method. As long as those methods cooperate with each other, they collectively provide a cohesive functionality:
import json
from typing import Self
class JSONSerializableMixin:
@classmethod
def from_json(cls, json_string: str) -> Self:
return cls(**json.loads(json_string))
def as_json(self) -> str:
return json.dumps(vars(self))
Here, you’ve added a class method to your mixin, allowing the target class to deserialize a JSON string into its respective instance. Both methods, .from_json() and .as_json(), enable two-way conversion between JSON strings and Python objects. Together, they represent one logical feature.
To help achieve their agnostic design, most mixins are typically implemented as simple Python classes without extending any parent type—much like plain old Java objects (POJO). Additionally, they make few assumptions about the classes they’ll eventually be mixed into. This keeps them largely unaware of their potential descendants, allowing for greater flexibility.
You can safely include one mixin in completely unrelated data types across diverse contexts. For example, you might reuse your JSONSerializableMixin in a class representing an application’s configuration, like so:
>>> from pathlib import Path
>>> from types import SimpleNamespace
>>> class AppSettings(JSONSerializableMixin, SimpleNamespace):
... def save(self, filepath: str | Path) -> None:
... Path(filepath).write_text(self.as_json(), encoding="utf-8")
...
>>> settings = AppSettings()
>>> settings.host = "localhost"
>>> settings.port = 8080
>>> settings.debug_mode = True
>>> settings.log_file = None
>>> settings.urls = (
... "https://192.168.1.200:8000",
... "https://192.168.1.201:8000",
... )
>>> settings.save("settings.json")
You define a subtype of SimpleNamespace, which is a lightweight, mutable container with a clean string representation and useful defaults. It lets you dynamically add or remove attributes from its instances and access them through the vars() function.
Note: As the name implies, SimpleNamespace should only be used in simple use cases. While it’s quick and convenient for prototyping, it can lead to errors due to naming typos or missing attributes. In the real world, you’d want to represent your configuration with more robust tools, such as data classes or a TypedDict, which offer better structure and validation.
Despite sharing an ancestor, neither AppSettings nor your earlier User data class should be considered part of the same lineage. Remember that you’re using Python’s class inheritance here solely for code reuse, without enforcing a strict parent-child relationship. Formally, the JSONSerializableMixin remains detached from the type tree.
This starkly contrasts with abstract base classes (ABCs), which are base classes meant to be subclassed and used polymorphically. They participate in subtyping and often have parent classes themselves. The word abstract means that ABCs declare abstract methods without implementing them to establish a contract for subclasses. At the same time, ABCs may provide partial implementations through regular methods and can maintain internal state.
Furthermore, declaring an ABC requires the use of either a special metaclass or a decorator, which Python can leverage to run additional checks for you:
>>> from abc import ABC, abstractmethod
>>> from typing import Any
>>> class Serializer(ABC):
... @abstractmethod
... def serialize(self, data: Any) -> str:
... pass
...
>>> abc_instance = Serializer()
Traceback (most recent call last):
...
TypeError: Can't instantiate abstract class Serializer
⮑ without an implementation for abstract method 'serialize'
As long as a class contains abstract or unimplemented methods, you won’t be able to instantiate it without triggering a runtime error. This mechanism enforces the contract on any concrete subclass.
Note: Python provides an alternative way to define contracts without using abstract base classes or inheritance. If you don’t require runtime type checks and code reuse, then consider specifying the expected behavior structurally with a Python protocol.
To sum up, you use abstract base classes to define the core implementation of an algorithm while enforcing a common interface across subclasses. This makes ABCs especially well-suited for implementing the template method pattern. Mixins, on the other hand, provide independent features that you can optionally add to other classes without altering their primary inheritance structure.
While abstract base classes frequently rely on single inheritance to define a clear interface and behavior, mixins often leverage multiple inheritance to seamlessly compose many features. But do you always need multiple inheritance to use mixins in Python? You’ll find out next.
Do Mixins Require Multiple Inheritance in Python?
By now, you know that mixins are designed to provide additional behaviors to other classes through inheritance without being standalone classes by themselves. You’ve also seen two basic examples, demonstrating how mixins can be used with both single and multiple inheritance:
# Single inheritance:
class User(JSONSerializableMixin):
...
# Multiple inheritance:
class AppSettings(JSONSerializableMixin, SimpleNamespace):
...
In the first example, the User class has only one parent—the mixin itself. But in the second example, the AppSettings class inherits from the same mixin and another base class at the same time.
Note: Conceptually, AppSettings is a more specialized type of SimpleNamespace with an additional capability to serialize itself to JSON. The JSONSerializableMixin has no knowledge of AppSettings, User, or any other descendant type that you may potentially plug it into.
The latter case is generally more common. After all, the main idea behind mixins is to include lightweight, isolated, and reusable features in classes that already extend one or more superclasses without disrupting their existing type hierarchies.
It’s also entirely possible for a class to simultaneously include more than one mixin, effectively letting you fuse several independent behaviors into a single class. That’s precisely what Python’s multiple inheritance enables—combining methods and attributes of more than one parent class. Many Python frameworks leverage this pattern a lot.
Here’s a sample implementation of a web page that displays a list of books in a hypothetical Django application:
django-book-project/library/views.py
from django.views.generic import ListView
from django.contrib.auth.mixins import (
LoginRequiredMixin,
PermissionRequiredMixin,
)
from .models import Book
class BookListView(LoginRequiredMixin, PermissionRequiredMixin, ListView):
model = Book
paginate_by = 10
permission_required = "library.view_book"
In this code snippet, you use mixins with class-based views to compose the desired functionality from modular behaviors declaratively. Instead of repeating similar authentication and authorization code across multiple views, you leverage mixins provided by Django, which abstract away the common logic.
Note: As a rule of thumb, mixins shouldn’t inherit anything from parent classes to remain self-contained and flexible. In some cases, however, you may find it convenient to collect their common parts into a base class. That’s the case with Django’s LoginRequiredMixin and PermissionRequiredMixin, both of which extend AccessMixin to reuse the same helper methods and attributes.
In summary, you don’t strictly need to rely on multiple inheritance to benefit from mixins in Python. That being said, mixins are most effective when you include them alongside other base classes, which can sometimes lead to surprising behavior.
Next up, you’ll learn about some of the dangers of working with mixins in Python and how to cope with them.
What Are the Common Pitfalls of Using Python Mixins?
Beneath their deceptively straightforward appearance, mixins introduce subtle complexities that can trip up even experienced developers. In this section, you’ll review some of the most common challenges associated with Python mixins. You’ll also learn how to mitigate them.
Going Overboard With Mixins
Like every tool in software engineering, mixins are a double-edged sword. When used skillfully, they improve code reuse, separation of concerns, and maintainability, promoting an elegant, declarative style. At the same time, it’s easy to overdo them, leading to a poor design with diminishing returns in the long run.
If you’ve worked on enough Django projects, then chances are you’ve come across code that looks something like this:
django-book-project/library/views.py
# ...
class BookOverloadedView(
LoginRequiredMixin,
PermissionRequiredMixin,
FormMixin,
SingleObjectMixin,
MultipleObjectMixin,
FilterView,
ExportMixin,
AuditLogMixin,
AjaxResponseMixin,
OptimisticLockingMixin,
UpdateView,
):
model = Book
form_class = BookForm
permission_required = "library.change_book"
filterset_class = BookFilter
success_url = "/books/"
Spotting so many mixins in one place is a good indicator of a God object with too many responsibilities that would be better off refactored. For instance, you might decouple it by grouping related functionalities that are frequently used together into higher-level classes.
Including more than a few mixins at a time increases your code’s complexity, making it harder to understand and debug. Imagine trying to follow the flow of execution in this codebase, for example, to determine where a given method was implemented. Because of an extremely tangled inheritance hierarchy, that would be a non-trivial task.
Even worse, what if some of those mixins canceled each other out or stepped on each other’s toes? The observed behavior would depend on the order of their declaration, which is due to how method resolution works in Python. This brings you to another common risk associated with mixins, which you’ll look into now.
Ordering Mixins Incorrectly
Most mixins encapsulate orthogonal behaviors, meaning they operate independently and can typically be combined in any order. Occasionally, though, mixins may introduce conflicting implementations or rely on superclass methods being invoked in a specific sequence. This requires careful attention to their order in the inheritance list.
The two mixins you saw earlier, LoginRequiredMixin and PermissionRequiredMixin, are designed to work seamlessly together, no matter how you arrange them in the class definition. While both override the same .dispatch() method, they actively participate in cooperative multiple inheritance by calling Python’s super(). As a result, they form a chain of method calls to ensure that each mixin’s logic gets executed at some point.
Nevertheless, the official Django documentation recommends always including LoginRequiredMixin before any other mixins that could be triggered prematurely:
When using class-based views, you can achieve the same behavior as with
login_requiredby using theLoginRequiredMixin. This mixin should be at the leftmost position in the inheritance list. (Source)
That sounds logical because Python follows base classes from left to right. After all, you’d want to check if the user has logged in before running any subsequent checks related to that user. Consider the following example, which violates this advice:
django-book-project/library/views.py
# ...
class BookDetailView(UserPassesTestMixin, LoginRequiredMixin, DetailView):
model = Book
def test_func(self):
return self.request.user.email.endswith("@realpython.com")
Notice that LoginRequiredMixin appears second in the class declaration, just after UserPassesTestMixin. This mixin allows you to define a custom test that determines whether a user has permission to access the view. In this case, you verify that the user’s email address belongs to the @realpython.com domain.
The trouble with this code is that anonymous users in Django don’t come with an .email attribute. So, when an unauthenticated user tries to access this view, your test function will raise an AttributeError, resulting in an internal server error (HTTP 500) being displayed in the browser.
The preferred behavior would be to redirect unauthenticated users to the login page, giving them a chance to prove their identity before proceeding to the email domain check. You can achieve this goal by flipping the two mixin declarations in your view:
django-book-project/library/views.py
# ...
class BookDetailView(LoginRequiredMixin, UserPassesTestMixin, DetailView):
model = Book
def test_func(self):
return self.request.user.email.endswith("@realpython.com")
Now, the web framework will ensure that the user is authenticated before checking their email address. In the worst-case scenario, after logging in, the user won’t have sufficient permissions to access the view and will receive a forbidden response (HTTP 403) that you can gracefully handle.
Note: The Django documentation features another interesting case, illustrating that it’s impossible to use multiple instances of UserPassesTestMixin in the inheritance chain:
class TestMixin1(UserPassesTestMixin):
def test_func(self):
return self.request.user.email.endswith("@example.com")
class TestMixin2(UserPassesTestMixin):
def test_func(self):
return self.request.user.username.startswith("django")
class MyView(TestMixin1, TestMixin2, View): ...
Attempting to stack them like this won’t work. Only the first mixin’s .test_func() will run, overriding the others. You’ll learn more about this in a bit.
Whenever your program’s behavior relies on implicit factors like the ordering of declarations, it makes your code fragile. Unfortunately, it’s an unavoidable aspect of multiple inheritance, which can manifest itself in even more nefarious ways. You’re about to explore a related gotcha of misplacing mixins in the class header.
Placing Mixins After Base Classes
Among the many challenges of using mixins, this is arguably the one that even seasoned Python developers fall victim to. Mixins should generally appear before other base classes in the inheritance list. Such ordering ensures that their methods are correctly invoked, either overriding or augmenting their counterparts in the base classes.
The only exception to this rule is when a mixin provides unique methods that are guaranteed not to conflict with those of other classes.
Note: Python takes advantage of the C3 superclass linearization algorithm (pronounced “C cubed”) to determine a predictable method resolution order (MRO) when multiple inheritance is involved. This provides an elegant solution to the infamous diamond problem, which caused ambiguity in languages like C++ and led Java to adopt only single inheritance.
To illustrate this common mistake, imagine that you were to implement a data container for storing HTTP headers in Python. You may choose to create a custom case-insensitive mapping of key-value pairs—for example, by inheriting from UserDict as follows:
utils.py
from collections import UserDict
class CaseInsensitiveDict(UserDict):
def __setitem__(self, key: str, value: str) -> None:
super().__setitem__(key.lower(), value)
def __getitem__(self, key: str) -> str:
return super().__getitem__(key.lower())
def __delitem__(self, key: str) -> None:
super().__delitem__(key.lower())
def __contains__(self, key: str) -> bool:
return super().__contains__(key.lower())
def get(self, key: str, default: str = "") -> str:
return super().get(key.lower(), default)
Your CaseInsensitiveDict overrides several special methods, which the parent class relies on internally, making sure that all keys are stored in lowercase form. That way, you can access and manipulate headers using any capitalization, such as "cookie" or "CooKIE":
>>> from utils import CaseInsensitiveDict
>>> headers = CaseInsensitiveDict()
>>> headers["Content-Type"] = "application/json"
>>> headers["Cookie"] = "csrftoken=a4f3c7d28c194e5b; sessionid=f92e4b7c6"
>>> headers["cookie"]
'csrftoken=a4f3c7d28c194e5b; sessionid=f92e4b7c6'
>>> "CooKIE" in headers
True
Now, say that you want to hook into some of these special methods through a mixin class that prints out useful debug messages:
utils.py
from collections import UserDict
class DebugMixin:
def __setitem__(self, key, value):
super().__setitem__(key, value)
print(f"Item set: {key=!r}, {value=!r}")
def __delitem__(self, key):
super().__delitem__(key)
print(f"Item deleted: {key=!r}")
class CaseInsensitiveDict(UserDict, DebugMixin):
# ...
The DebugMixin delegates execution to the corresponding superclass methods before printing messages whenever items in a collection are modified. To add this new behavior to your CaseInsensitiveDict, you append the mixin to the list of base classes in your class definition.
Unfortunately, after restarting the Python REPL and importing the updated utils module again, you won’t see any change in the observed behavior:
>>> from utils import CaseInsensitiveDict
>>> headers = CaseInsensitiveDict()
>>> headers["Content-Type"] = "application/json"
>>> headers["Cookie"] = "csrftoken=a4f3c7d28c194e5b; sessionid=f92e4b7c6"
>>> del headers["Cookie"]
>>> headers
{'content-type': 'application/json'}
The dictionary still lets you add and remove key-value pairs as expected, but none of the debug messages show up. That’s because the methods defined in your DebugMixin are never called. Python doesn’t invoke them since UserDict break