- Avoid using multiple inheritance if mix-in classes can achieve the same outcome.
Effective Python
Mix-in Mix-in is a small class that only define a set of additional methods that a class should provide.
In object-oriented programming languages, a mixin is a class that contains methods for use by other classes without having to be the parent class of those other classes. How those other classes gain access to the mixin's methods depends on the language. Mixins are sometimes described as being "included" rather than "inherited".
Mixins encourage code reuse and can be used to avoid the inheritance ambiguity that multiple inheritance can cause 1, or to work around lack of support for multiple inheritance in a language. A mixin can also be viewed as an interface with implemented methods. This pattern is an example of enforcing the dependency inversion principle.
https://en.wikipedia.org/wiki/Mixin
What is a mixin, and why are they useful?
http://stackoverflow.com/questions/533631/what-is-a-mixin-and-why-are-they-useful
A mixin is a special kind of multiple inheritance. There are two main situations where mixins are used:
You want to provide a lot of optional features for a class. You want to use one particular feature in a lot of different classes.
import time
class ToDictMixin:
def to_dict(self):
return self._traverse_dict(self.__dict__)
def _traverse_dict(self, instance_dict):
output = {}
for key, value in instance_dict.items():
output[key] = self._traverse(key, value)
return output
def _traverse(self, key, value):
time.sleep(0.2)
if isinstance(value, ToDictMixin):
print('ToDictMixin')
return value.to_dict()
elif isinstance(value, dict):
print('dict')
return self._traverse_dict(value)
elif isinstance(value, list):
print('list')
return [self._traverse(key, i) for i in value]
elif hasattr(value, '__dict__'):
print('hasattr __dict__')
return self._traverse_dict(value.__dict__)
else:
return value
class BinaryTree(ToDictMixin):
def __init__(self, value, left=None, right=None):
self.value = value
self.left = left
self.right = right
tree = BinaryTree(10, left=BinaryTree(7, right=BinaryTree(9)),
right=BinaryTree(13, left=BinaryTree(11)))
# print(tree.to_dict())
Without overiding _traverse
, BinaryTreeWithParent causes stack overflow by infinite loop.
to_dict -> _traverse_dict -> _traverse(isinstance of ToDictMixin) -> to_dict -> _travese_dict -> _traverse(isinstance of ToDitctMixin)...
class BinaryTreeWithParent(BinaryTree):
def __init__(self, value, left=None, right=None, parent=None):
super().__init__(value, left=left, right=right)
self.parent = parent
def _traverse(self, key, value):
if (isinstance(value, BinaryTreeWithParent) and key == 'parent'):
return value.value
else:
return super()._traverse(key, value)
root = BinaryTreeWithParent(10)
root.left = BinaryTreeWithParent(7, parent=root)
root.left.right = BinaryTreeWithParent(9, parent=root.left)
print(root.to_dict())
NamedSubTree also inherits ToDictMixin. However _travese
behavior of BinaryTreeWithParent is not changed due to the polymorphism.
class NamedSubTree(ToDictMixin):
def __init__(self, name, tree_with_parent):
self.name = name
self.tree_with_parent = tree_with_parent
my_tree = NamedSubTree('foobar', root)
print(my_tree.to_dict())