Skip to content

Commit 276c21f

Browse files
committed
enum done
and readme change category to content readme move learning material down
1 parent a948f51 commit 276c21f

20 files changed

Lines changed: 217 additions & 37 deletions

File tree

BasicObject/bytearray/bytearray.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# bytearray
22

3-
### category
3+
### contents
44

55
* [related file](#related-file)
66
* [memory layout](#memory-layout)

BasicObject/bytes/bytes.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# bytes
22

3-
### category
3+
### contents
44

55
* [related file](#related-file)
66
* [memory layout](#memory-layout)

BasicObject/class/class.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# class
22

3-
### category
3+
### contents
44

55
* [related file](#related-file)
66
* [memory layout](#memory-layout)

BasicObject/complex/complex.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# complex
22

3-
### category
3+
### contents
44

55
* [related file](#related-file)
66
* [memory layout](#memory-layout)

BasicObject/dict/dict.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# dict
22

3-
### category
3+
### contents
44

55
Because the **PyDictObject** is a little bit more complicated than other basic object, I will not show _\_setitem_\_/_\_getitem_\_ step by step, instead, I will illustrate in the middle of some concept
66

BasicObject/enum/enum.md

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# enum
22

3-
### category
3+
### contents
44

55
* [related file](#related-file)
66
* [memory layout](#memory-layout)
@@ -33,7 +33,7 @@
3333
>>> type(e)
3434
<class 'enumerate'>
3535

36-
before iter through the object **e**, the **en_index** field is 0, **en_sit** stores the actual generator object being iterated, **en_result** stores the previous result
36+
before iter through the object **e**, the **en_index** field is 0, **en_sit** stores the actual generator object being iterated, **en_result** points a tuple object with two empty value
3737

3838
we will see the meaning of **en_longindex** later
3939

@@ -45,7 +45,7 @@ we will see the meaning of **en_longindex** later
4545
>>> id(t1)
4646
4469348888
4747

48-
now, the **en_index** becomes 1, the tuple in **en_result** is the last tuple object returned, the elements in the tuple are changed, but the address **en_result** doesn't change, not because of the [free-list mechanism in tuple](https://github.com/zpoint/CPython-Internals/blob/master/BasicObject/tuple/tuple.md#free-list)
48+
now, the **en_index** becomes 1, the tuple in **en_result** is the last tuple object returned, the elements in the tuple are changed, but the address in **en_result** doesn't change, not because of the [free-list mechanism in tuple](https://github.com/zpoint/CPython-Internals/blob/master/BasicObject/tuple/tuple.md#free-list)
4949

5050
it's a trick in the **enumerate** iterating function
5151

@@ -85,7 +85,9 @@ it's a trick in the **enumerate** iterating function
8585
return result;
8686
}
8787

88-
it's clear, because only the current enumerate object keep a reference to the old `tuple object -> (None, None)` **enum_next** reset the 0th element in the tuple to 0, and 1th element in the tuple to 'I', so the address **en_result** points to is the same
88+
it's clear, because only the current enumerate object keep a reference to the old `tuple object -> (None, None)` object
89+
90+
**enum_next** reset the 0th element in the tuple to 0, and 1th element in the tuple to 'I', so the address **en_result** points to is the same
8991

9092
![example1](https://github.com/zpoint/CPython-Internals/blob/master/BasicObject/enum/example1.png)
9193

@@ -147,4 +149,5 @@ what **en_longindex** points to is a [PyLongObject(python type int)](https://git
147149

148150
>>> e = enumerate(gen(), (1 << 63) + 100)
149151

150-
![longindex1](https://github.com/zpoint/CPython-Internals/blob/master/BasicObject/enum/longindex1.png)
152+
![longindex1](https://github.com/zpoint/CPython-Internals/blob/master/BasicObject/enum/longindex1.png)
153+

BasicObject/enum/enum_cn.md

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
# enum
2+
3+
### 目录
4+
5+
* [相关位置文件](#相关位置文件)
6+
* [内存构造](#内存构造)
7+
* [示例](#示例)
8+
* [normal](#normal)
9+
* [en_longindex](#en_longindex)
10+
11+
#### 相关位置文件
12+
13+
* cpython/Objects/enumobject.c
14+
* cpython/Include/enumobject.h
15+
* cpython/Objects/clinic/enumobject.c.h
16+
17+
#### 内存构造
18+
19+
**enumerate** 是一个类型, **enumerate** 的实例是一个可迭代对象, 你可以在迭代的过程中同时获得这个迭代的对象和一个计数器
20+
21+
![layout](https://github.com/zpoint/CPython-Internals/blob/master/BasicObject/enum/layout.png)
22+
23+
#### 示例
24+
25+
##### normal
26+
27+
def gen():
28+
yield "I"
29+
yield "am"
30+
yield "handsome"
31+
32+
e = enumerate(gen())
33+
34+
>>> type(e)
35+
<class 'enumerate'>
36+
37+
在迭代对象 **e** 之前, 它的 **en_index** 字段为 0, **en_sit** 指向了真正的 **generator** 对象, **en_result** 指向了一个有两个空值的 **tuple**
38+
39+
我们后面会提到 **en_longindex** 的作用
40+
41+
![example0](https://github.com/zpoint/CPython-Internals/blob/master/BasicObject/enum/example0.png)
42+
43+
>>> t1 = next(e)
44+
>>> t1
45+
(0, 'I')
46+
>>> id(t1)
47+
4469348888
48+
49+
现在 **en_index** 字段变成了 1, **en_result** 里面指向的元组对象为最近一次返回的元组对象, 元组里面的两个元素都改变了, 但是 **en_result** 里的地址没有变化, 没有变化的原因不是 [tuple 缓冲池](https://github.com/zpoint/CPython-Internals/blob/master/BasicObject/tuple/tuple_cn.md#free-list) 机制的原因
50+
51+
而是和 **enumerate** 的迭代函数使用的技巧有关
52+
53+
static PyObject *
54+
enum_next(enumobject *en)
55+
{
56+
/* omit */
57+
PyObject *result = en->en_result;
58+
/* omit */
59+
if (result->ob_refcnt == 1) {
60+
/*
61+
tuple 对象的引用计数器为 is 1
62+
说明当前唯一引用这个 tuple 对象的就是这个 enumerate 实例本身
63+
既然这个 tuple 对象的两个旧元素已经不需要了
64+
我们可以把这两个元素设置为新的元素, 并返回这个 tuple 对象
65+
*/
66+
Py_INCREF(result);
67+
old_index = PyTuple_GET_ITEM(result, 0);
68+
old_item = PyTuple_GET_ITEM(result, 1);
69+
PyTuple_SET_ITEM(result, 0, next_index);
70+
PyTuple_SET_ITEM(result, 1, next_item);
71+
Py_DECREF(old_index);
72+
Py_DECREF(old_item);
73+
return result;
74+
}
75+
/*
76+
到达这里, 这个 tuple 对象的引用计数器不为 1, 处理自己本身还有其他的变量在使用它
77+
我们不能重置这个 tuple 里面的元素
78+
只能创建新的返回给调用者
79+
*/
80+
result = PyTuple_New(2);
81+
if (result == NULL) {
82+
Py_DECREF(next_index);
83+
Py_DECREF(next_item);
84+
return NULL;
85+
}
86+
PyTuple_SET_ITEM(result, 0, next_index);
87+
PyTuple_SET_ITEM(result, 1, next_item);
88+
return result;
89+
}
90+
91+
很明显了, 因为旧的 **tuple** `tuple object -> (None, None)` 对象唯一的引用来自当前的 **enumerate** 对象, **enum_next** 会把这个 **tuple** 对象的第 0 个元素变为 0, 第 1 个元素变为 'I', 之后把这个旧 tuple 返回
92+
93+
所以 **en_result** 指向的地址未发生改变, 并且 **en_result** 指向的对象为这次迭代返回的对象
94+
95+
![example1](https://github.com/zpoint/CPython-Internals/blob/master/BasicObject/enum/example1.png)
96+
97+
这个 **tuple** 对象 `(0, 'I') # id(4469348888)` 现在的引用计数器变为 2 了, 一个来自 **enumerate** 实例的引用, 还有一个来自变量名称 t1, **enum_next** 这次会进入下面的分支, 创建一个新的 **tuple** 对象并返回而不是重置那个旧的 **tuple** 对象
98+
99+
**en_index** 中的数值增加了, **en_result** 仍然指向这个旧的 **tuple** 对象`(0, 'I') # id(4469348888)`
100+
101+
>>> next(e)
102+
(1, 'am')
103+
104+
![example2](https://github.com/zpoint/CPython-Internals/blob/master/BasicObject/enum/example2.png)
105+
106+
`del t1` 语句执行之后, 这个 tuple 对象 `(0, 'I') # id(4469348888)` 的引用计数器变回了 1, 此时 **enum_next** 会像上面一样重置这个 **tuple** 对象, **en_result** 仍然指向这个对象, 并且这次返回的为 **en_result** 指向的对象
107+
108+
>>> del t1 # decrement the reference count of the object referenced by t1
109+
>>> next(e)
110+
(2, 'handsome')
111+
112+
![example3](https://github.com/zpoint/CPython-Internals/blob/master/BasicObject/enum/example3.png)
113+
114+
结束标记是被 **en_sit** 里面存储的对象所存储的, **enumerate** 本身不存储结束标记等信息
115+
116+
>>> next(e)
117+
Traceback (most recent call last):
118+
File "<stdin>", line 1, in <module>
119+
StopIteration
120+
121+
![example3](https://github.com/zpoint/CPython-Internals/blob/master/BasicObject/enum/example3.png)
122+
123+
##### en_longindex
124+
125+
通常情况下, 计数器的值是存储在 **en_index** 里面的, **en_index** 的类型是 **Py_ssize_t**, 我们来看下 **Py_ssize_t** 的定义
126+
127+
#ifdef HAVE_SSIZE_T
128+
typedef ssize_t Py_ssize_t;
129+
#elif SIZEOF_VOID_P == SIZEOF_SIZE_T
130+
typedef Py_intptr_t Py_ssize_t;
131+
#else
132+
# error "Python needs a typedef for Py_ssize_t in pyport.h."
133+
#endif
134+
135+
大部分情况下他是一个 **ssize_t**, 32位操作系统下为 int, 64位下为 **long int**
136+
137+
在我的机器上他是 **long int**
138+
139+
如果这个计数器的值大到这 64个 bit 都装不下呢?
140+
141+
e = enumerate(gen(), 1 << 62)
142+
143+
这个时候 **en_index** 是可以放得下这个值的
144+
145+
![longindex0](https://github.com/zpoint/CPython-Internals/blob/master/BasicObject/enum/longindex0.png)
146+
147+
**en_index** 能表示的最大的值为 ((1 << 63) - 1) (PY_SSIZE_T_MAX)
148+
149+
现在实际上的计数器的值已经比 PY_SSIZE_T_MAX 还要大了, 此时 **en_longindex** 会被用来存储真正的计数器
150+
151+
**en_longindex** 指向的是一个 [PyLongObject(python 类型 int)](https://github.com/zpoint/CPython-Internals/blob/master/BasicObject/long/long_cn.md), 对象, 可以表示任意长度的整数大小
152+
153+
>>> e = enumerate(gen(), (1 << 63) + 100)
154+
155+
![longindex1](https://github.com/zpoint/CPython-Internals/blob/master/BasicObject/enum/longindex1.png)
156+

BasicObject/enum/example3.png

63.6 KB
Loading

BasicObject/float/float.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# float
22

3-
### category
3+
### contents
44

55
* [related file](#related-file)
66
* [memory layout](#memory-layout)

BasicObject/func/func.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# func
22

3-
### category
3+
### contents
44

55
* [related file](#related-file)
66
* [memory layout](#memory-layout)

0 commit comments

Comments
 (0)