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 = nameCode 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 = dependentsCode 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 valueCode 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 = dependentsCode 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 = dependentsCode 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())

Comments

Leave a Reply

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