PyDictObject即字典对象,类似于C++ STL中的map,但STL中以红黑树实现,Python中dict以hash表(散列表)实现。
散列表,通过Hash函数将特定对象映射为特定数字;当装载率大于2/3时,散列冲突概率增加,解决散列冲突,STL采用开链法,而Python采用开放定址法。开放定址法法,在探测冲突链上依次跳转,如果删除探测冲突链上某个元素,会使探测冲突链断裂。故而,删除某元素时,不可在物理上真正删除。
1、entry与dict对象
typedef struct {
Py_ssize_t me_hash; // key的hash值
PyObject *me_key;
PyObject *me_value;
} PyDictEntry;
struct _dictobject {
PyObject_HEAD
Py_ssize_t ma_fill; /* # Active + # Dummy */
Py_ssize_t ma_used; /* # Active */
Py_ssize_t ma_mask; // dict对象所拥有的entry数量
PyDictEntry *ma_table; // 指向存放元素的内存区域
PyDictEntry *(*ma_lookup)(PyDictObject *mp, PyObject *key, long hash); // 搜索函数指针
PyDictEntry ma_smalltable[PyDict_MINSIZE]; // 自带少量元素内存
};
2、搜索策略
由于采用开放定址法,探测冲突链上元素不可删除,Python将每个元素设为三种状态。从未使用是unused,正在使用是Active,当将某个Active的元素删除时将其置为dummy。
通过ma_key与ma_value的值来判定状态。
Python每种类型的对象都实现了各自的hash函数,如string对象函数函数如下:
int len = strObject->length;
unsignedchar * p = (unsignedchar *)strObject->value;
long x = *p << 7;
while (--len >= 0)
x = (1000003*x) ^ *p++;
x ^= strObject->length;
if (x == -1)
x = -2;
strObject->hashValue = x;
for (perturb = hash; ; perturb >>= 5) {
i = (i << 2) + i + perturb + 1;
ep = &ep0[i & mask];
}
3、dict对象使用
dict对象的各种应用均依赖与搜索策略,而在python中使用PyStringObject*做key的情况特别多,python默认使用针对string优化的搜索函数,如果key不是string对象,返回通用搜索函数。
元素的插入,需注意dict对象会自动调整内存大小,以装载率大于2/3为基准。调整大小时会重新分布元素,故dummy态不再需要,被销毁。
元素的删除,需注意该元素置为dummuy态。
元素的设置,若存在该key,直接更改value;若不存在,添加该entry,即(key,value)。
4、dict对象缓冲池
和list对象缓冲池类似。