一、列表
PyListObject 是一个可变对象,支持插入和删除,并且可以动态的调整其做维护的内存大小。
"它并不是存了多少东西就申请对应大小的内存,这样的申请策略显然是低效的,因为我们有理由相信,用户选用列表正是为了频繁地插入删除元素 。因此,PyListObject 在每一次需要申请内存的时候,会申请一大块内存,这是申请的总内存的大小记录在allocated 中,而这些内存中实际被有效的 PyObject*占用的内存大小被记录在了 ob_size 中 "
0<=ob_size<=allocated
len(ob_item)=ob_size
ob_item所指向的内存空间是想数组一样的连续内存区域。所以,再第i个位置插入元素,会导致第i个及其后的所有元素都往后移动。
插入关键代码
static int ins1(PyListObject *self, int where, PyObject *v)
{ int i, n = self->ob_size; PyObject **items;
items = self->ob_item; for (i = n; --i >= where; ) items[i+1] = items[i];
items[where] = v;
return 0;
}
同理,删除元素时,第i+1及其后的元素需要往前移动。
删除关键代码
item = a->ob_item;
if (d < 0) {
/* Delete -d items */
memmove(&item[ihigh+d], &item[ihigh], (a->ob_size - ihigh)*sizeof(PyObject *));
item = a->ob_item;
}
二、字典
PyDictObject 使用散列表组织有关联规则的元素对。在最优情况下,散列表能提供 O(1)复杂度的搜索效率 。
(一)entry
散列表的核心思想是:用于搜索的键通过散列函数映射整数,该整数作为索引指向成片的内存区域,用于存储值,如下图所示:
所以,每个存放键值对的关联容器(entry)数据结构如下所示:
typedef struct {
long me_hash;
PyObject *me_key;
PyObject *me_value;
} PyDictEntry;
随着键值对的插入和删除,entry的状态变化图如下所示:
(二)由entry组成的PyDictObject 对象
一个 PyDictObject 对象实际上是一大堆用散列表组织起来的 entry 的集合 。
ma_used:Active和Dummy状态的entry 的数量
ma_fill:Active状态的entry 的数量
ma_mask:记录了一个 PyDictObject 对象中有 entry 的数量
ma_lookup:搜索策略函数
无论是插入还是删除,都会先调用 PyDictObject的ma_lookup搜索策略函数查找元素。
插入关键代码(有删减)
static void insertdict(register dictobject *mp, PyObject *key, long hash, PyObject *value)
{
PyObject *old_value;
register dictentry *ep;
ep = mp->ma_lookup(mp, key, hash);
if (ep->me_value != NULL) {
old_value = ep->me_value;
ep->me_value = value;
} //[1]
else {
if (ep->me_key == NULL) //Unused
mp->ma_fill++;
else //Dummy
Py_DECREF(ep->me_key);
ep->me_key = key;
ep->me_hash = hash;
ep->me_value = value;
mp->ma_used++;
} //[2]
}
[1] :搜索成功,返回处于 Active 的 entry,直接替换 me_value。
[2] :搜索失败,返回 Unused 或 Dummy 的 entry,完整设置 me_key, me_hash
和 me_value。
删除关键代码
int PyDict_DelItem(PyObject *op, PyObject *key){
register dictobject *mp;
register long hash;
register dictentry *ep;
PyObject *old_value, *old_key;
//获得 hash 值 ...
//搜索 entry
mp = (dictobject *)op;
ep = (mp->ma_lookup)(mp, key, hash);
//删除 entry 所维护的元素
old_key = ep->me_key;
Py_INCREF(dummy);
ep->me_key = dummy;
old_value = ep->me_value;
ep->me_value = NULL;
mp->ma_used--;
Py_DECREF(old_value);
Py_DECREF(old_key);
return 0;
}
搜索出要删除的元素,设置entry节点为Dummy态,也即ma_key=dummy,ma_value=NULL。
PyDictObject的搜索策略函数,查看下篇。