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值,還需要更進一步的比較值才可以。