-
-
Notifications
You must be signed in to change notification settings - Fork 525
/
mro.py
113 lines (82 loc) · 3.37 KB
/
mro.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
"""
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()