Summary: in this tutorial, you’ll learn about Python mixin classes and how to use them to make the code reusable.
What is a mixin in Python
A mixin is a class that provides method implementations for reuse by multiple related child classes. However, the inheritance is not implying an is-a relationship.
A mixin doesn’t define a new type. Therefore, it is not intended for direction instantiation.
A mixin bundles a set of methods for reuse. Each mixin should have a single specific behavior, implementing closely related methods.
Typically, a child class uses multiple inheritance to combine the mixin classes with a parent class.
Since Python doesn’t define a formal way to define mixin classes, it’s a good practice to name mixin classes with the suffix Mixin
.
A mixin class is like an interface in Java and C# with implementation. And it’s like a trait in PHP.
Python Mixin example
First, define a Person
class:
class Person:
def __init__(self, name):
self.name = name
Code language: Python (python)
Second, define an Employee
class that inherits from the Person
class:
class Employee(Person):
def __init__(self, name, skills, dependents):
super().__init__(name)
self.skills = skills
self.dependents = dependents
Code language: Python (python)
Third, create a new instance of the Employee
class:
if __name__ == '__main__':
e = Employee(
name='John',
skills=['Python Programming''Project Management'],
dependents={'wife': 'Jane', 'children': ['Alice', 'Bob']}
)
Code language: Python (python)
Suppose you want to convert the Employee
object to a dictionary. To do that, you can add a new method to the Employee
class, which converts the object to a dictionary.
However, you may want to convert objects of other classes to dictionaries. To make the code reusable, you can define a mixin class called DictMixin
like the following:
class DictMixin:
def to_dict(self):
return self._traverse_dict(self.__dict__)
def _traverse_dict(self, attributes: dict) -> dict:
result = {}
for key, value in attributes.items():
result[key] = self._traverse(key, value)
return result
def _traverse(self, key, value):
if isinstance(value, DictMixin):
return value.to_dict()
elif isinstance(value, dict):
return self._traverse_dict(value)
elif isinstance(value, list):
return [self._traverse(key, v) for v in value]
elif hasattr(value, '__dict__'):
return self._traverse_dict(value.__dict__)
else:
return value
Code language: Python (python)
The DictMixin
class has the to_dict()
method that converts an object to a dictionary.
The _traverse_dict()
method iterates the object’s attributes and assigns the key and value to the result.
The attribute of an object may be a list, a dictionary, or an object with the __dict__
attribute. Therefore, the _traverse_dict()
method uses the _traverse()
method to convert the attribute to value.
To convert instances of the Employee
class to dictionaries, the Employee
needs to inherit from both DictMixin
and Person
classes:
class Employee(DictMixin, Person):
def __init__(self, name, skills, dependents):
super().__init__(name)
self.skills = skills
self.dependents = dependents
Code language: Python (python)
Note that you need to specify the mixin classes before other classes.
The following creates a new instance of the Employee
class and converts it to a dictionary:
e = Employee(
name='John',
skills=['Python Programming', 'Project Management'],
dependents={'wife': 'Jane', 'children': ['Alice', 'Bob']}
)
pprint(e.to_dict())
Code language: Python (python)
Output:
{'dependents': {'children': ['Alice', 'Bob'], 'wife': 'Jane'},
'name': 'John',
'skills': ['Python Programming', 'Project Management']}
Code language: Python (python)
The following shows the complete code:
from pprint import pprint
class DictMixin:
def to_dict(self):
return self._traverse_dict(self.__dict__)
def _traverse_dict(self, attributes):
result = {}
for key, value in attributes.items():
result[key] = self._traverse(key, value)
return result
def _traverse(self, key, value):
if isinstance(value, DictMixin):
return value.to_dict()
elif isinstance(value, dict):
return self._traverse_dict(value)
elif isinstance(value, list):
return [self._traverse(key, v) for v in value]
elif hasattr(value, '__dict__'):
return self._traverse_dict(value.__dict__)
else:
return value
class Person:
def __init__(self, name):
self.name = name
class Employee(DictMixin, Person):
def __init__(self, name, skills, dependents):
super().__init__(name)
self.skills = skills
self.dependents = dependents
if __name__ == '__main__':
e = Employee(
name='John',
skills=['Python Programming', 'Project Management'],
dependents={'wife': 'Jane', 'children': ['Alice', 'Bob']}
)
pprint(e.to_dict())
Code language: Python (python)
Compose multiple mixin classes
Suppose you want to convert the Employee
‘s object to JSON. To do that, you can first define a new mixin class that use the json
standard module:
import json
class JSONMixin:
def to_json(self):
return json.dumps(self.to_dict())
Code language: Python (python)
And then change the Employee
class so that it inherits the JSONMixin
class:
class Employee(DictMixin, JSONMixin, Person):
def __init__(self, name, skills, dependents):
super().__init__(name)
self.skills = skills
self.dependents = dependents
Code language: Python (python)
The following creates a new instance of the Employee
class and converts it to a dictionary and json:
if __name__ == '__main__':
e = Employee(
name='John',
skills=['Python Programming''Project Management'],
dependents={'wife': 'Jane', 'children': ['Alice', 'Bob']}
)
pprint(e.to_dict())
print(e.to_json())
Code language: Python (python)
Output:
{'dependents': {'children': ['Alice', 'Bob'], 'wife': 'Jane'},
'name': 'John',
'skills': ['Python ProgrammingProject Management']}
{"name": "John", "skills": ["Python ProgrammingProject Management"], "dependents": {"wife": "Jane", "children": ["Alice", "Bob"]}}
Code language: Python (python)
The following shows the complete code:
import json
from pprint import pprint
class DictMixin:
def to_dict(self):
return self._traverse_dict(self.__dict__)
def _traverse_dict(self, attributes):
result = {}
for key, value in attributes.items():
result[key] = self._traverse(key, value)
return result
def _traverse(self, key, value):
if isinstance(value, DictMixin):
return value.to_dict()
elif isinstance(value, dict):
return self._traverse_dict(value)
elif isinstance(value, list):
return [self._traverse(key, v) for v in value]
elif hasattr(value, '__dict__'):
return self._traverse_dict(value.__dict__)
else:
return value
class JSONMixin:
def to_json(self):
return json.dumps(self.to_dict())
class Person:
def __init__(self, name):
self.name = name
class Employee(DictMixin, JSONMixin, Person):
def __init__(self, name, skills, dependents):
super().__init__(name)
self.skills = skills
self.dependents = dependents
if __name__ == '__main__':
e = Employee(
name='John',
skills=['Python Programming''Project Management'],
dependents={'wife': 'Jane', 'children': ['Alice', 'Bob']}
)
pprint(e.to_dict())
print(e.to_json())
Code language: Python (python)
Summary
- A mixin class provides method implementions for resuse by multiple related subclasses.