forked from huangsam/ultimate-python
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmro.py
More file actions
106 lines (75 loc) · 3.29 KB
/
mro.py
File metadata and controls
106 lines (75 loc) · 3.29 KB
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
"""
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()