Python中Dict的查找


Dict的類型的查找使用的是lookdict函數

static PyDictKeyEntry *
lookdict(PyDictObject *mp, PyObject *key,
         Py_hash_t hash, PyObject ***value_addr)

函數的參數中,*value_addr是指向匹配slot中值的指針。 這個函數在正確的情況下一定會返回一個指向slot的指針,出錯則會返回NULL。 如果成功找到了匹配的slot,則返回對應的slot; 如果沒有匹配的slot,則返回查找鏈上第一個未被使用的slot。 該slot可以是unused狀態,也可以是dummy狀態。

    mask = DK_MASK(mp->ma_keys);
    ep0 = &mp->ma_keys->dk_entries[0];
    i = (size_t)hash & mask;

計算了slot的初始位置,把hash值映射到slot table的下標范圍內。 初始位置=hash&mask,mask=dk_size-1

    if (ep->me_key == NULL || ep->me_key == key) {
        *value_addr = &ep->me_value;
        return ep;
    }

如果找到了匹配的key或unused slot,返回該結果即可。

    if (ep->me_key == dummy)
        freeslot = ep;
    else {
        if (ep->me_hash == hash) {
            startkey = ep->me_key;
            Py_INCREF(startkey);
            cmp = PyObject_RichCompareBool(startkey, key, Py_EQ);
            Py_DECREF(startkey);
            if (cmp < 0)
                return NULL;
            if (ep0 == mp->ma_keys->dk_entries && ep->me_key == startkey) {
                if (cmp > 0) {
                    *value_addr = &ep->me_value;
                    return ep;
                }
            }
            else {
                /* The dict was mutated, restart */
                goto top;
            }
        }
        freeslot = NULL;
    }

進一步的比較。 若該slot狀態為dummy,則用freeslot記錄該slot並繼續搜索; 如果該slot的hash值與待搜索key的hash相同,那么對兩個key進行比較。 這里的PyObject_RichCompareBool是一個比較函數,其第三個參數為比較的操作。 如果操作結果為true,返回1;為false,返回0;比較出錯,返回-1。 比較出錯的情況下會返回NULL,比較成功(在這里為相等)返回該slot,比較不成功則繼續進行搜索。 這一部分進行了第一次的搜索;在dict容量不太滿時,一般在這里就可以找到合適的結果。

        i = (i << 2) + i + perturb + 1;
        ep = &ep0[i & mask];
        if (ep->me_key == NULL) {
            if (freeslot == NULL) {
                *value_addr = &ep->me_value;
                return ep;
            } else {
                *value_addr = &freeslot->me_value;
                return freeslot;
            }
        }

找到了unused slot的情況。 如果freeslot是NULL,那么返回該slot即可;若freeslot不是NULL,那么返回freeslot。

        if (ep->me_key == key) {
            *value_addr = &ep->me_value;
            return ep;
        }

找到了匹配的key。此情況返回對應slot即可。

        if (ep->me_hash == hash && ep->me_key != dummy) {
            startkey = ep->me_key;
            Py_INCREF(startkey);
            cmp = PyObject_RichCompareBool(startkey, key, Py_EQ);
            Py_DECREF(startkey);
            if (cmp < 0) {
                *value_addr = NULL;
                return NULL;
            }
            if (ep0 == mp->ma_keys->dk_entries && ep->me_key == startkey) {
                if (cmp > 0) {
                    *value_addr = &ep->me_value;
                    return ep;
                }
            }
            else {
                /* The dict was mutated, restart */
                goto top;
            }
        }

該slot hash值與給定hash值相同時進一步比較的情況。

        else if (ep->me_key == dummy && freeslot == NULL)
            freeslot = ep;

在dummy情況下設置freeslot。

 

在搜索過程中,原則是找到和key相等的對象即可。 那么什么是和key相等呢? 一種情況是它們的引用相等,自然的值也相等。 這類比較只需要直接比較對應指針是否相等呢該即可。 而另一種情況是引用不相等,但值還相等。 如果沒有對這種情況的處理,那么對於非共享的對象來說搜索幾乎不會得到正確的結果。 搜索中的進一步比較就是對這種情況的處理。 進一步比較發生的前提是hash值相等,因為值相等必然有hash相等, 但hash相等值卻可能不等,因此不能直接比較hash值,還需要更進一步的比較值才可以。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM