Last active
July 7, 2017 22:42
-
-
Save carlos-jenkins/21fe92389198d409756890e702ec5d34 to your computer and use it in GitHub Desktop.
Collaborative constructors for multiple inheritance.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
""" | |
Collaborative constructors for multiple inheritance. | |
Python 3 only. If using Python 2.7, collaborative constructor are broken for | |
positional arguments. | |
Collaborative constructors allow classes to collaborate in the way constructor | |
arguments are passed between all classes in the inheritance tree. | |
Two aspects are crucial in the constructors: | |
1. In the signature of the constructors, type: | |
def __init__(self, [positional arguments], *args, [keyword arguments], **kwargs): | |
So, make sure you put the *args AFTER the positional arguments, and BEFORE | |
the keyword arguments. | |
2. At the end of the constructor, call the constructor of its parent EVEN IF | |
the parent is the good old object base class, with the remaining positional | |
and keyword arguments. | |
Basically, collaborative constructors "consume" their relevant arguments | |
(positional and keyword) and pass the rest to the other classes in the | |
inheritance tree. | |
One final aspect is crucial when defining the multiple inheritance: | |
1. The call execution is determined by the way you placed the parents. | |
It WILL run from left to right, depth first. | |
That is, for example: | |
class Parent1(object): | |
pass | |
class GrandParent(object): | |
pass | |
class Parent2(GrandParent): | |
pass | |
class Parent3(object): | |
pass | |
class MyLeaf(Parent1, Parent2, Parent3): | |
pass | |
If you implement collaborative constructors the execution sequence will be: | |
1. MyLeaf. | |
2. Parent1. | |
3. Parent2. | |
4. GrandParent. | |
5. Parent3. | |
When executed, this script will print: | |
TheClass has arg tcarg1 and kwarg tckwarg1-init | |
MixinTwo has arg m2arg1 and kwarg m2kwarg1-init | |
MixinChildOne has arg mc1arg1 and kwarg mc1kwarg1-init | |
MixinOne has arg m1arg1 and kwarg m1kwarg1-init | |
ChildOne has arg c1arg1 and kwarg c1kwarg1-init | |
BaseOne has arg b1arg1 and kwarg b1kwarg1-init | |
""" # noqa | |
class BaseOne(object): | |
def __init__(self, b1arg1, *args, b1kwarg1='b1kwarg1', **kwargs): | |
self._b1arg1 = b1arg1 | |
self._b1kwarg = b1kwarg1 | |
print('{} has arg {} and kwarg {}'.format( | |
'BaseOne', b1arg1, b1kwarg1 | |
)) | |
super().__init__(*args, **kwargs) | |
class BaseTwo(object): | |
def __init__(self, b2arg1, *args, b2kwarg1='b2kwarg1', **kwargs): | |
self._b2arg1 = b2arg1 | |
self._b2kwarg = b2kwarg1 | |
print('{} has arg {} and kwarg {}'.format( | |
'BaseTwo', b2arg1, b2kwarg1 | |
)) | |
super().__init__(*args, **kwargs) | |
class ChildOne(BaseOne): | |
def __init__(self, c1arg1, *args, c1kwarg1='c1kwarg1', **kwargs): | |
self._c1arg1 = c1arg1 | |
self._c1kwarg = c1kwarg1 | |
print('{} has arg {} and kwarg {}'.format( | |
'ChildOne', c1arg1, c1kwarg1 | |
)) | |
super().__init__(*args, **kwargs) | |
class MixinOne(object): | |
def __init__(self, m1arg1, *args, m1kwarg1='m1kwarg1', **kwargs): | |
self._m1arg1 = m1arg1 | |
self._m1kwarg1 = m1kwarg1 | |
print('{} has arg {} and kwarg {}'.format( | |
'MixinOne', m1arg1, m1kwarg1 | |
)) | |
super().__init__(*args, **kwargs) | |
class MixinChildOne(MixinOne): | |
def __init__(self, mc1arg1, *args, mc1kwarg1='mc1kwarg1', **kwargs): | |
self._mc1arg1 = mc1arg1 | |
self._mc1kwarg1 = mc1kwarg1 | |
print('{} has arg {} and kwarg {}'.format( | |
'MixinChildOne', mc1arg1, mc1kwarg1 | |
)) | |
super().__init__(*args, **kwargs) | |
class MixinTwo(object): | |
def __init__(self, m2arg1, *args, m2kwarg1='m2kwarg1', **kwargs): | |
self._m2arg1 = m2arg1 | |
self._m2kwarg1 = m2kwarg1 | |
print('{} has arg {} and kwarg {}'.format( | |
'MixinTwo', m2arg1, m2kwarg1 | |
)) | |
super().__init__(*args, **kwargs) | |
class TheClass(MixinTwo, MixinChildOne, ChildOne, BaseOne): | |
def __init__(self, tcarg1, *args, tckwarg1='tckwarg1', **kwargs): | |
self._tcarg1 = tcarg1 | |
self._tckwarg1 = tckwarg1 | |
print('{} has arg {} and kwarg {}'.format( | |
'TheClass', tcarg1, tckwarg1 | |
)) | |
super().__init__(*args, **kwargs) | |
tc = TheClass( | |
# Positional arguments must be passed as left to right, depth first. | |
'tcarg1', | |
'm2arg1', | |
'mc1arg1', | |
'm1arg1', | |
'c1arg1', | |
'b1arg1', | |
# Keyword arguments can be passed in any order. | |
tckwarg1='tckwarg1-init', | |
m2kwarg1='m2kwarg1-init', | |
mc1kwarg1='mc1kwarg1-init', | |
m1kwarg1='m1kwarg1-init', | |
c1kwarg1='c1kwarg1-init', | |
b1kwarg1='b1kwarg1-init', | |
) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment