Mro
MRO stands for method resolution order and it's used by class definitions to determine which method will be run by a class instance. This module shows how the MRO is useful for the classic diamond problem where classes B and C depend on class A, and class D depends on classes B and C.
class BasePlayer:
"""Base player."""
def ping(self):
return "ping"
def pong(self):
return "pong"
class PongPlayer(BasePlayer):
"""Pong player."""
def pong(self):
return "PONG"
class NeutralPlayer(BasePlayer):
"""Neutral player."""
class ConfusedPlayer(PongPlayer, NeutralPlayer):
"""Confused player.
This is what we call the diamond problem, where `BasePlayer` child classes
are the same as `ConfusedPlayer` parent classes. Python has the MRO to
determine which `ping` and `pong` methods are called via the `super()`
call followed by the respective method.
The `super()` call is usually used without any parameters, which
means that we start the MRO process from the current class upwards.
For more on the subject, please consult this link:
https://www.python.org/download/releases/2.3/mro/
"""
def ping(self):
"""Override `ping` method."""
return "pINg"
def ping_pong(self):
"""Run `ping` and `pong` in different ways."""
return [
self.ping(),
super().ping(),
self.pong(),
super().pong()
]
class IndecisivePlayer(NeutralPlayer, PongPlayer):
"""Indecisive player.
Notice that this class was created successfully without any conflicts
even though the MRO of `ConfusedPlayer` is different.
Notice that one of the `super()` calls uses additional parameters to
start the MRO process from another class. This is generally discouraged
as this bypasses the default method resolution process.
"""
def pong(self):
"""Override `pong` method."""
return "pONg"
def ping_pong(self):
"""Run `ping` and `pong` in different ways."""
return [
self.ping(),
super().ping(),
self.pong(),
super(PongPlayer, self).pong() # bypass MRO to `BasePlayer`
]
def main():
# `ConfusedPlayer` methods are resolved from child to parent like this
assert ConfusedPlayer.mro() == [
ConfusedPlayer, PongPlayer, NeutralPlayer, BasePlayer, object]
# `IndecisivePlayer` methods are resolved from child to parent like this
assert IndecisivePlayer.mro() == [
IndecisivePlayer, NeutralPlayer, PongPlayer, BasePlayer, object]
# Show `ConfusedPlayer` method resolution in action
assert ConfusedPlayer().ping_pong() == ["pINg", "ping", "PONG", "PONG"]
# Show `IndecisivePlayer` method resolution in action
assert IndecisivePlayer().ping_pong() == ["ping", "ping", "pONg", "pong"]
class_creation_failed = False
try:
# Creating a new class `ConfusedPlayer` and `IndecisivePlayer`
# results in a `TypeError` because both classes do not have
# matching MRO outputs. This means that they cannot be reconciled
# as one class. Hence `MissingPlayer` will not be created
type("MissingPlayer", (ConfusedPlayer, IndecisivePlayer), {})
except TypeError:
class_creation_failed = True
assert class_creation_failed is True
if __name__ == "__main__":
main()