Python mixin

Created with Sketch.

Python mixin

 

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.

Leave a Reply

Your email address will not be published. Required fields are marked *