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())
Leave a Reply