- Python's standard method resolution order (MRO) solves the problems of superclass intialization order and diamond inheritance.
- Always use the
super
built-in function to initialize parent classes.
Effective Python
Early python 2
from pprint import pprint
class MyBaseClass:
def __init__(self, value):
self.value = value
class TimesTwo:
def __init__(self):
self.value *= 2
class PlusFive:
def __init__(self):
self.value += 5
class OneWay(MyBaseClass, TimesTwo, PlusFive):
def __init__(self, value):
MyBaseClass.__init__(self, value)
TimesTwo.__init__(self)
PlusFive.__init__(self)
print(OneWay(5).value)
Two problems:
- Multiple inheritance
- Diamond inheritance
Multiple inheritance
class OneWay(MyBaseClass, TimesTwo, PlusFive):
def __init__(self, value):
MyBaseClass.__init__(self, value)
TimesTwo.__init__(self)
PlusFive.__init__(self)
We can initilize class against the order of inheritance.
class OneWay(MyBaseClass, TimesTwo, PlusFive):
def __init__(self, value):
MyBaseClass.__init__(self, value)
PlusFive.__init__(self)
TimesTwo.__init__(self)
MRO: Method Resolution Order
Diamond inheritance with bad practice
# diamond inheritance
class TimesFive(MyBaseClass):
def __init__(self, value):
MyBaseClass.__init__(self, value)
self.value *= 5
class PlusTwo(MyBaseClass):
def __init__(self, value):
MyBaseClass.__init__(self, value)
self.value += 2
class ThisWay(TimesFive, PlusFive):
def __init__(self, value):
TimesFive.__init__(self, value)
PlusTwo.__init__(self, value)
foo = ThisWay(5)
print(foo.value)
Expected:
5 * 5 + 2 = 27
Actual:
7
However, we get 7 because self.value
is initialized TimesFive.__init__(self, value)
and PlusTwo.__init__(self, value)
.
super
# Python2
class TimesFiveCorrect(MyBaseClass):
def __init__(self, value):
super(TimesFiveCorrect, self).__init__(value)
self.value *= 5
class PlusTwoCorrect(MyBaseClass):
def __init__(self, value):
super(PlusTwoCorrect, self).__init__(value)
self.value += 2
# Python 2
class GoodWay(TimesFiveCorrect, PlusTwoCorrect):
def __init__(self, value):
super(GoodWay, self).__init__(value)
foo = GoodWay(5)
print(foo.value)
print(GoodWay.mro())
>>>
[<class '__main__.GoodWay'>,
<class '__main__.TimesFiveCorrect'>,
<class '__main__.PlusTwoCorrect'>,
<class '__main__.MyBaseClass'>,
<class 'object'>]
__class__
does not work in Python2.
# Python 3
class Explicit(MyBaseClass):
def __init__(self, value):
super(__class__, self).__init__(value * 2)
class Implicit(MyBaseClass):
def __init__(self, value):
super().__init__(value * 2)