forked from zpoint/CPython-Internals
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtype.md
More file actions
254 lines (167 loc) · 7.47 KB
/
Copy pathtype.md
File metadata and controls
254 lines (167 loc) · 7.47 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
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
# type
# contents
* [related file](#related-file)
* [mro](#mro)
* [before python2.3](#before-python2.3)
* [after python2.3](#after-python2.3)
* [difference bwtween python2 and python3](#difference-bwtween-python2-and-python3)
* [creation of class](#creation-of-class)
* [creation of instance](#creation-of-instance)
* [metaclass](#metaclass)
* [read more](#read-more)
# related file
* cpython/Objects/typeobject.c
* cpython/Objects/clinic/typeobject.c.h
* cpython/Include/cpython/object.h
* cpython/Python/bltinmodule.c
# mro
the [C3 superclass linearization](https://en.wikipedia.org/wiki/C3_linearization) is implemented in python for Method Resolution Order (MRO) after python2.3, prior to python2.3, the Method Resolution Order is a Depth-First, Left-to-Right algorithm
for those who are interested in detail, please refer to [Amit Kumar: Demystifying Python Method Resolution Order - PyCon APAC 2016](https://www.youtube.com/watch?v=cuonAMJjHow&t=400s)
## before python2.3
let's define an example
from __future__ import print_function
class A(object):
pass
class B(object):
def who(self):
print("I am B")
class C(object):
pass
class D(A, B):
pass
class E(B, C):
def who(self):
print("I am E")
class F(D, E):
pass

the **mro** order implementation before python2.3 is a Depth-First, Left-Most algorithm
mro of **D** is computed as `DAB`
mro of **E** is computed as `EBC`
mro of **F** is computed as `FDABEC`
# ./python2.2
>>> f = F()
>>> f.who() # B is in front of E
I am B
## after python2.3
the **merge** part of the **C3** algorithm can be simply summarized as the following steps
* take the head of the first list
* if this head is not in the tail of any of the other lists, then add it to the linearization of C and remove it from the lists in the merge
* otherwise look at the head of the next list and take it, if it is a good head
* then repeat the operation until all the class are removed or it is impossible to find good heads(the inherted order need to be monotonous, or the algorithm won't terminate, for more detail please refer to the first video in [read more](#read-more))
mro of **D** is computed as
D + merge(L(A), L(B), AB)
D + merge(A, B, AB)
# A is the head of the next list, and not the tail of any other list, take A out, remove A in the lists in the merge
D + A + merge(B, B)
# the first element is the list is the head, not the tail, so B is taken
D + A + B
mro of **E** is computed as
E + merge(L(B), L(C), BC)
E + merge(B, C, BC)
E + B + merge(C, C)
E + B + C
mro of **F** is computed as
F + merge(L(D), L(E), BC)
F + merge(DAB, EBC, DE)
F + D + merge(AB, EBC, E)
F + D + A + merge(B, EBC, E)
# B is now in the tail of the second list, the head of the second list is "E"
# tail is "BC", so B can't be taken, next head E will be taken
F + D + A + E + merge(B, BC)
F + D + A + E + B + C
# ./python2.3
>>> f = F()
>>> f.who() # E is in front of B
I am E
## difference bwtween python2 and python3
in the following code
from __future__ import print_function
class A:
pass
class B:
def who(self):
print("I am B")
class C:
pass
class D(A, B):
pass
class E(B, C):
def who(self):
print("I am E")
class F(D, E):
pass
in python2, class `A`, `B`,`C`, `D`, and `E` are not inherted from the `object`, even after python2.3, objects not inherted from `object` will not use the **C3** algorithm for **MRO**, the old style Depth-First, Left-Most algorithm will be used
# ./python2.7
>>> f = F()
>>> f.who() # B is in front of E
I am B
while in python3, they both implicit inherted from `object`, objects inherted from `object` use the **C3** algorithm for **MRO**
# ./python3
>>> f = F()
>>> f.who() # E is in front of B
I am E
# creation of class
if we `dis` the above code
26 96 LOAD_BUILD_CLASS
98 LOAD_CONST 12 (<code object F at 0x10edf4150, file "mro.py", line 26>)
100 LOAD_CONST 13 ('F')
102 MAKE_FUNCTION 0
104 LOAD_CONST 13 ('F')
106 LOAD_NAME 6 (D)
108 LOAD_NAME 7 (E)
110 CALL_FUNCTION 4
112 STORE_NAME 8 (F)
`LOAD_BUILD_CLASS` simply pushes the function `__build_class__` to stack
and the following opcodes push all the variables `__build_class__` needed to stack
`110 CALL_FUNCTION 4` calls the `__build_class__` to generate the class
the `__build_class__` will find the `metaclass`, `name`, `bases`, and `ns`(namespace) and delegates the call to `metaclass.__call__` attribute
for example, the `class F`
metaclass: <class 'type'>
name: 'F'
bases: (<class '__main__.D'>, <class '__main__.E'>)
ns: {'__module__': '__main__', '__qualname__': 'F'}, cls: <class '__main__.F'>
in the example `class F`, what does `<class 'type'>.__call__` do ?
the function is defined in `cpython/Objects/typeobject.c`
prototype is `static PyObject *type_call(PyTypeObject *type, PyObject *args, PyObject *kwds)`
we can draw the following procedure according to the source code

# creation of instance
if we add the following line to the tail of the above codes
f = F()
we can find other snippet of the `dis` result
31 130 LOAD_NAME 9 (F)
132 CALL_FUNCTION 0
134 STORE_NAME 10 (f)
it simply calls the `__call__` attribute of `F`
>>> F.__call__
<method-wrapper '__call__' of type object at 0x7fa5fa725ec8>

# metaclass
in the above procedures, we can learn that **metaclass** controls the creation of a **class**, the creation of **instance** doesn't invoke the **metaclass**, it just calls the `__call__` attribute of the class to create the instance

we can change the behaviour of a class on the fly by manually define a **metaclass**
class MyMeta(type):
def __new__(mcs, name, bases, attrs, **kwargs):
if F in bases:
return F
newly_created_cls = super().__new__(mcs, name, bases, attrs)
if "Animal" in name:
newly_created_cls.leg = 4
else:
newly_created_cls.leg = 0
return newly_created_cls
class Animal1(metaclass=MyMeta):
pass
class Normal(metaclass=MyMeta):
pass
class AnotherF(F, metaclass=MyMeta):
pass
print(Animal1.leg) # 4
print(Normal.leg) # 0
print(AnotherF) # <class '__main__.F'>
pretty fun!
# read more
* [Amit Kumar: Demystifying Python Method Resolution Order - PyCon APAC 2016](https://www.youtube.com/watch?v=cuonAMJjHow&t=400s)
* [The Python 2.3 Method Resolution Order(MRO)](https://www.python.org/download/releases/2.3/mro/)
* [understanding-python-metaclasses](https://blog.ionelmc.ro/2015/02/09/understanding-python-metaclasses)