整型
1 | PyTypeObject PyLong_Type = { |
- python3统一采用长整型8 bit
- id() 内存地址; is 是判断内存地址id是否相等,==判断内容是否相等
- 相同代码块的缓存机制 int str bool 使用相同的常量池对象
- 在不同代码块下,采用小整数驻留机制[-5,256]
- 该tp_new函数应该调用subtype->tp_alloc(subtype, nitems)为对象分配空间。于不可变类型,所有初始化都应该在tp_new,而对于可变类型,大多数初始化应该延迟到tp_init。
字符串
1 | PyObject * |
字符串对象的共享机制intern
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# 字符串创建
void PyUnicode_InternInPlace(PyObject **p)
{
PyObject *s = *p;
PyObject *t;
if (s == NULL || !PyUnicode_Check(s))
return;
// 对PyUnicodeObjec进行类型和状态检查
if (!PyUnicode_CheckExact(s))
return;
if (PyUnicode_CHECK_INTERNED(s))
return;
// 创建intern机制的dict
if (interned == NULL) {
interned = PyDict_New();
if (interned == NULL) {
PyErr_Clear(); /* Don't leave an exception */
return;
}
}
// 对象是否存在于inter中
t = PyDict_SetDefault(interned, s, s);
// 存在, 调整引用计数
if (t != s) {
Py_INCREF(t);
Py_SETREF(*p, t);
return;
}
/* The two references in interned are not counted by refcnt.
The deallocator will take care of this */
Py_REFCNT(s) -= 2;
_PyUnicode_STATE(s).interned = SSTATE_INTERNED_MORTAL;
}
[unicodeobjec.c]
PyObject * PyUnicode_DecodeUTF8Stateful(const char *s,
Py_ssize_t size,
const char *errors,
Py_ssize_t *consumed)
{
...
/* ASCII is equivalent to the first 128 ordinals in Unicode. */
if (size == 1 && (unsigned char)s[0] < 128) {
if (consumed)
*consumed = 1;
return get_latin1_char((unsigned char)s[0]);
}
...
}
static PyObject *unicode_latin1[256] = {NULL};
static PyObject* get_latin1_char(unsigned char ch)
{
PyObject *unicode = unicode_latin1[ch];
if (!unicode) {
unicode = PyUnicode_New(1, ch);
if (!unicode)
return NULL;
PyUnicode_1BYTE_DATA(unicode)[0] = ch;
assert(_PyUnicode_CheckConsistency(unicode, 1));
unicode_latin1[ch] = unicode;
}
Py_INCREF(unicode);
return unicode;
}
总结:先对所创建的字符串(只有一个字符)对象进行intern操作,再将inter的结果缓存到字符缓冲池 unicode_latin1 中,两者都是指向同一个字符对象。intern机制中的PyUnicodObject采用了特殊的引用计数机制。将一个PyUnicodeObject对象a的PyObject指针作为key和valu添加到intered中时,PyDictObjec对象会通过这两个指针对a的引用计数进行两次+1操作。这会造成a的引用计数在python程序结束前永远不会为0,这也是 Py_REFCNT(s) -= 2; 将计数减2的原因。
字符串对象有两种状态,一个是 SSTATE_INTERNED_IMMORTAL 另一个是 SSTATE_INTERNED_MORTAL, 处于SSTATE_INTERNED_IMMORTAL这种状态的字符串永远不会被销毁,它将与python虚拟机共存亡。 PyUnicode_InternInPlace 函数只能创建SSTATE_INTERNED_MORTAL 状态的对象
python中使用 “+” 符号进行字符串拼接, 这种方法效率极低,建议使用jion
list
1 | typedef struct { |
- 缓冲池 free_list 中最多会维护80个PyListObject对象
1
2
3#define PyList_MAXFREELIST 80
static PyListObject *free_list[PyList_MAXFREELIST];
static int numfree = 0; - 设置元素 list[2] = 200
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24int PyList_SetItem(PyObject *op, Py_ssize_t i,
PyObject *newitem)
{
PyObject **p;
// 索引越界检查
if (i < 0 || i >= Py_SIZE(op)) {
Py_XDECREF(newitem);
PyErr_SetString(PyExc_IndexError,
"list assignment index out of range");
return -1;
}
// 找到ob_item[i]
p = ((PyListObject *)op) -> ob_item + i;
Py_XSETREF(*p, newitem);
return 0;
}
#define Py_XSETREF(op, op2) \
do { \
PyObject *_py_tmp = (PyObject *)(op); \
(op) = (op2); \
Py_XDECREF(_py_tmp); \
} while (0) - 插入元素
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[longobject.c]
int PyList_Insert(PyObject *op, Py_ssize_t where, PyObject *newitem)
{
// 类型检查
if (!PyList_Check(op)) {
PyErr_BadInternalCall();
return -1;
}
return ins1((PyListObject *)op, where, newitem);
}
static int
ins1(PyListObject *self, Py_ssize_t where, PyObject *v)
{
Py_ssize_t i, n = Py_SIZE(self);
PyObject **items;
if (list_resize(self, n+1) < 0)
return -1;
// 确定插入点
if (where < 0) {
where += n;
if (where < 0)
where = 0;
}
if (where > n)
where = n;
items = self->ob_item;
// 元素后移
for (i = n; --i >= where; )
items[i+1] = items[i];
Py_INCREF(v);
items[where] = v;
return 0;
} - 删除元素
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20static PyObject *
listremove(PyListObject *self, PyObject *v)
{
Py_ssize_t i;
for (i = 0; i < Py_SIZE(self); i++) {
int cmp = PyObject_RichCompareBool(self->ob_item[i], v, Py_EQ);
// 如果相等, 删除元素
if (cmp > 0) {
if (list_ass_slice(self, i, i+1,
(PyObject *)NULL) == 0)
Py_RETURN_NONE;
return NULL;
}
else if (cmp < 0)
return NULL;
}
PyErr_SetString(PyExc_ValueError, "list.remove(x): x not in list");
return NULL;
}
Dict
闭散列法也称为开放定址法.当产生冲突时,python会通过一个二次探测函数f,计算下一个候选索引, 如果索引不可用,就再次用f探测.直到找到一个可用的位置.
1 | [dict-common.h] |
PyDict_MINSIZE 值为8, 默认情况下, PyDict_New会创建8个entry
Unused: me_key == me_value == NULL (Unused是entry的初始状态,key和value都为NULL。插入元素时,Unused状态转换成Active状态。这是me_key为NULL的唯一情况。)
Active: me_key != NULL and me_key != dummy 且 me_value != NULL(插入元素后,entry就成了Active状态,这是me_value唯一不为NULL的情况,删除元素时Active状态刻转换成Dummy状态。)
Dummy: me_key == dummy 且 me_value == NULL(Dummy是一种类似的伪删除方式,保证探测链的连续性 ABC 删除B 保证A->c)
lookdict(k,v)
1. index <- hash1(k),freeslot<-Null,根据me_key与me_value选择2、3、4一个执行; 2. 查看index处的值处于’有效元素占据‘状态,判断data[index]与v是否一致(地址或内容),一致,则返回查找成功;否则转5 3. index所指向的位置处于’原始空‘状态,查找失败,若freeslot==Null返回index;否则返回freeslot;转5 4. index所指向的位置处于’有效元素离去‘状态,freeslot<-index, 转5 5. index <- hash2(index),,转2
加载因子 2/3 ,大于则扩容