Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 56693ec

Browse files
committed
ch04
1 parent 5d54800 commit 56693ec

File tree

1 file changed

+279
-0
lines changed

1 file changed

+279
-0
lines changed

‎ch04.md‎

Lines changed: 279 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,281 @@
11
# Python 中的 List 对象
22

3+
PyListObject 定义如下:
4+
5+
```C
6+
typedef struct {
7+
PyObject_VAR_HEAD
8+
/* Vector of pointers to list elements. list[0] is ob_item[0], etc. */
9+
PyObject **ob_item;
10+
11+
/* ob_item contains space for 'allocated' elements. The number
12+
* currently in use is ob_size.
13+
* Invariants:
14+
* 0 <= ob_size <= allocated
15+
* len(list) == ob_size
16+
* ob_item == NULL implies ob_size == allocated == 0
17+
* list.sort() temporarily sets allocated to -1 to detect mutations.
18+
*
19+
* Items must normally not be NULL, except during construction when
20+
* the list is not yet visible outside the function that builds it.
21+
*/
22+
Py_ssize_t allocated;
23+
} PyListObject;
24+
```
25+
代码注释已经说得足够明白了。
26+
27+
## 创建 PyListObject
28+
29+
```C
30+
PyObject *
31+
PyList_New(Py_ssize_t size)
32+
{
33+
PyListObject *op;
34+
size_t nbytes;
35+
36+
if (size < 0) {
37+
PyErr_BadInternalCall();
38+
return NULL;
39+
}
40+
nbytes = size * sizeof(PyObject *);
41+
/* Check for overflow */
42+
if (nbytes / sizeof(PyObject *) != (size_t)size)
43+
return PyErr_NoMemory();
44+
if (num_free_lists) {
45+
num_free_lists--;
46+
op = free_lists[num_free_lists];
47+
_Py_NewReference((PyObject *)op);
48+
} else {
49+
op = PyObject_GC_New(PyListObject, &PyList_Type);
50+
if (op == NULL)
51+
return NULL;
52+
}
53+
if (size <= 0)
54+
op->ob_item = NULL;
55+
else {
56+
op->ob_item = (PyObject **) PyMem_MALLOC(nbytes);
57+
if (op->ob_item == NULL) {
58+
Py_DECREF(op);
59+
return PyErr_NoMemory();
60+
}
61+
memset(op->ob_item, 0, nbytes);
62+
}
63+
op->ob_size = size;
64+
op->allocated = size;
65+
_PyObject_GC_TRACK(op);
66+
return (PyObject *) op;
67+
}
68+
```
69+
70+
## 设置元素
71+
72+
```C
73+
int
74+
PyList_SetItem(register PyObject *op, register Py_ssize_t i,
75+
register PyObject *newitem)
76+
{
77+
register PyObject *olditem;
78+
register PyObject **p;
79+
if (!PyList_Check(op)) {
80+
Py_XDECREF(newitem);
81+
PyErr_BadInternalCall();
82+
return -1;
83+
}
84+
if (i < 0 || i>= ((PyListObject *)op) -> ob_size) {
85+
Py_XDECREF(newitem);
86+
PyErr_SetString(PyExc_IndexError,
87+
"list assignment index out of range");
88+
return -1;
89+
}
90+
p = ((PyListObject *)op) -> ob_item + i;
91+
olditem = *p;
92+
*p = newitem;
93+
Py_XDECREF(olditem);
94+
return 0;
95+
}
96+
```
97+
** 旧元素的 refcnt - 1**
98+
99+
## 插入新元素
100+
101+
```C
102+
static int
103+
ins1(PyListObject *self, Py_ssize_t where, PyObject *v)
104+
{
105+
Py_ssize_t i, n = self->ob_size;
106+
PyObject **items;
107+
if (v == NULL) {
108+
PyErr_BadInternalCall();
109+
return -1;
110+
}
111+
if (n == PY_SSIZE_T_MAX) {
112+
PyErr_SetString(PyExc_OverflowError,
113+
"cannot add more objects to list");
114+
return -1;
115+
}
116+
117+
if (list_resize(self, n+1) == -1)
118+
return -1;
119+
120+
if (where < 0) {
121+
where += n;
122+
if (where < 0)
123+
where = 0;
124+
}
125+
if (where> n)
126+
where = n;
127+
items = self->ob_item;
128+
for (i = n; --i>= where; )
129+
items[i+1] = items[i];
130+
Py_INCREF(v);
131+
items[where] = v;
132+
return 0;
133+
}
134+
135+
int
136+
PyList_Insert(PyObject *op, Py_ssize_t where, PyObject *newitem)
137+
{
138+
if (!PyList_Check(op)) {
139+
PyErr_BadInternalCall();
140+
return -1;
141+
}
142+
return ins1((PyListObject *)op, where, newitem);
143+
}
144+
```
145+
146+
插入元素的流程:
147+
148+
- 调整 PyListObject 的尺寸,以便容下新元素。
149+
150+
- 从最后一个元素开始,每个元素往后以一步,直至 where(新元素插入的位置)
151+
152+
- 将新元素放到 where。
153+
154+
list_resize 代码如下:
155+
156+
```C
157+
/* Ensure ob_item has room for at least newsize elements, and set
158+
* ob_size to newsize. If newsize > ob_size on entry, the content
159+
* of the new slots at exit is undefined heap trash; it's the caller's
160+
* responsiblity to overwrite them with sane values.
161+
* The number of allocated elements may grow, shrink, or stay the same.
162+
* Failure is impossible if newsize <= self.allocated on entry, although
163+
* that partly relies on an assumption that the system realloc() never
164+
* fails when passed a number of bytes <= the number of bytes last
165+
* allocated (the C standard doesn't guarantee this, but it's hard to
166+
* imagine a realloc implementation where it wouldn't be true).
167+
* Note that self->ob_item may change, and even if newsize is less
168+
* than ob_size on entry.
169+
*/
170+
static int
171+
list_resize(PyListObject *self, Py_ssize_t newsize)
172+
{
173+
PyObject **items;
174+
size_t new_allocated;
175+
Py_ssize_t allocated = self->allocated;
176+
177+
/* Bypass realloc() when a previous overallocation is large enough
178+
to accommodate the newsize. If the newsize falls lower than half
179+
the allocated size, then proceed with the realloc() to shrink the list.
180+
*/
181+
if (allocated>= newsize && newsize >= (allocated >> 1)) {
182+
assert(self->ob_item != NULL || newsize == 0);
183+
self->ob_size = newsize;
184+
return 0;
185+
}
186+
187+
/* This over-allocates proportional to the list size, making room
188+
* for additional growth. The over-allocation is mild, but is
189+
* enough to give linear-time amortized behavior over a long
190+
* sequence of appends() in the presence of a poorly-performing
191+
* system realloc().
192+
* The growth pattern is: 0, 4, 8, 16, 25, 35, 46, 58, 72, 88, ...
193+
*/
194+
new_allocated = (newsize>> 3) + (newsize < 9 ? 3 : 6) + newsize;
195+
if (newsize == 0)
196+
new_allocated = 0;
197+
items = self->ob_item;
198+
if (new_allocated <= ((~(size_t)0) / sizeof(PyObject *)))
199+
PyMem_RESIZE(items, PyObject *, new_allocated);
200+
else
201+
items = NULL;
202+
if (items == NULL) {
203+
PyErr_NoMemory();
204+
return -1;
205+
}
206+
self->ob_item = items;
207+
self->ob_size = newsize;
208+
self->allocated = new_allocated;
209+
return 0;
210+
}
211+
```
212+
213+
从代码中可以看出,为了避免频繁的调用 realloc,Python 会多分配一些空间。
214+
215+
## 添加元素
216+
217+
```C
218+
static int
219+
app1(PyListObject *self, PyObject *v)
220+
{
221+
Py_ssize_t n = PyList_GET_SIZE(self);
222+
223+
assert (v != NULL);
224+
if (n == PY_SSIZE_T_MAX) {
225+
PyErr_SetString(PyExc_OverflowError,
226+
"cannot add more objects to list");
227+
return -1;
228+
}
229+
230+
if (list_resize(self, n+1) == -1)
231+
return -1;
232+
233+
Py_INCREF(v);
234+
PyList_SET_ITEM(self, n, v);
235+
return 0;
236+
}
237+
238+
int
239+
PyList_Append(PyObject *op, PyObject *newitem)
240+
{
241+
if (PyList_Check(op) && (newitem != NULL))
242+
return app1((PyListObject *)op, newitem);
243+
PyErr_BadInternalCall();
244+
return -1;
245+
}
246+
```
247+
248+
## 删除元素
249+
250+
```C
251+
static PyObject *
252+
listremove(PyListObject *self, PyObject *v)
253+
{
254+
Py_ssize_t i;
255+
256+
for (i = 0; i < self->ob_size; i++) {
257+
int cmp = PyObject_RichCompareBool(self->ob_item[i], v, Py_EQ);
258+
if (cmp> 0) {
259+
if (list_ass_slice(self, i, i+1,
260+
(PyObject *)NULL) == 0)
261+
Py_RETURN_NONE;
262+
return NULL;
263+
}
264+
else if (cmp < 0)
265+
return NULL;
266+
}
267+
PyErr_SetString(PyExc_ValueError, "list.remove(x): x not in list");
268+
return NULL;
269+
}
270+
```
271+
272+
关键的是 list_ass_slice 函数,具体看源码,这里就粘贴了。
273+
274+
## PyListObject 对象缓冲池 free_lists
275+
276+
当销毁 PyListObject 对象时,只释放了 PyListObject 关联的 PyObject* 数组占有的内存,PyListObject 本身的内存没有释放,而是缓存在 free_lists 中。当下次创建 PyListObject 对象时,可以直接利用 free_lists 中的 PyListObject。
277+
278+
279+
280+
281+

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /