(Pyton学习5)Dict对象

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];      // 自带少量元素内存
};

元素数量不同时,可使用不同策略

(Pyton学习5)Dict对象_第1张图片

2、搜索策略
由于采用开放定址法,探测冲突链上元素不可删除,Python将每个元素设为三种状态。从未使用是unused,正在使用是Active,当将某个Active的元素删除时将其置为dummy。

(Pyton学习5)Dict对象_第2张图片

通过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;

产生散列冲突时,采用二次探测函数,Python中如下:
for (perturb = hash; ; perturb >>= 5) {
	i = (i << 2) + i + perturb + 1;
	ep = &ep0[i & mask];
}

冲突探测链开端为通过hash函数找到的第一个entry,结尾为第一个unused状态entry。
搜索整体思想为,在探测链上依次寻找;遇到dummy状态则记录为freesplot,并继续探测;直至探测到匹配的元素,返回该对象;或者直到最后一个元素即unused状态entry,此时若freesplot为空则返回该unused,若不为空则返回freesplot记录的dummy。
(Pyton学习5)Dict对象_第3张图片
有图示几种情况,1-6依次返回A、C、D、E、X、Y。


3、dict对象使用
dict对象的各种应用均依赖与搜索策略,而在python中使用PyStringObject*做key的情况特别多,python默认使用针对string优化的搜索函数,如果key不是string对象,返回通用搜索函数。
元素的插入,需注意dict对象会自动调整内存大小,以装载率大于2/3为基准。调整大小时会重新分布元素,故dummy态不再需要,被销毁。
元素的删除,需注意该元素置为dummuy态。
元素的设置,若存在该key,直接更改value;若不存在,添加该entry,即(key,value)。


4、dict对象缓冲池
和list对象缓冲池类似。


你可能感兴趣的:(Python)