最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Python字典對象實現(xiàn)原理詳解

 更新時間:2019年07月01日 09:47:42   作者:FOOFISH-PYTHON之禪  
這篇文章主要介紹了Python字典對象實現(xiàn)原理詳解,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下

字典類型是Python中最常用的數(shù)據(jù)類型之一,它是一個鍵值對的集合,字典通過鍵來索引,關(guān)聯(lián)到相對的值,理論上它的查詢復雜度是 O(1) :

>>> d = {'a': 1, 'b': 2}
>>> d['c'] = 3
>>> d
{'a': 1, 'b': 2, 'c': 3}

在字符串的實現(xiàn)原理文章中,曾經(jīng)出現(xiàn)過字典對象用于intern操作,那么字典的內(nèi)部結(jié)構(gòu)是怎樣的呢?PyDictObject對象就是dict的內(nèi)部實現(xiàn)。

哈希表 (HASH TABLES)

哈希表(也叫散列表),根據(jù)關(guān)鍵值對(Key-value)而直接進行訪問的數(shù)據(jù)結(jié)構(gòu)。它通過把key和value映射到表中一個位置來訪問記錄,這種查詢速度非???,更新也快。而這個映射函數(shù)叫做哈希函數(shù),存放值的數(shù)組叫做哈希表。 哈希函數(shù)的實現(xiàn)方式?jīng)Q定了哈希表的搜索效率。具體操作過程是:

1.數(shù)據(jù)添加:把key通過哈希函數(shù)轉(zhuǎn)換成一個整型數(shù)字,然后就將該數(shù)字對數(shù)組長度進行取余,取余結(jié)果就當作數(shù)組的下標,將value存儲在以該數(shù)字為下標的數(shù)組空間里。

2.數(shù)據(jù)查詢:再次使用哈希函數(shù)將key轉(zhuǎn)換為對應(yīng)的數(shù)組下標,并定位到數(shù)組的位置獲取value。

但是,對key進行hash的時候,不同的key可能hash出來的結(jié)果是一樣的,尤其是數(shù)據(jù)量增多的時候,這個問題叫做哈希沖突。如果解決這種沖突情況呢?通常的做法有兩種,一種是鏈接法,另一種是開放尋址法,Python選擇后者。

開放尋址法(OPEN ADDRESSING)

開放尋址法中,所有的元素都存放在散列表里,當產(chǎn)生哈希沖突時,通過一個探測函數(shù)計算出下一個候選位置,如果下一個獲選位置還是有沖突,那么不斷通過探測函數(shù)往下找,直到找個一個空槽來存放待插入元素。

PYDICTENTRY

字典中的一個key-value鍵值對元素稱為entry(也叫做slots),對應(yīng)到Python內(nèi)部是PyDictEntry,PyDictObject就是PyDictEntry的集合。PyDictEntry的定義是:

typedef struct {
/* Cached hash code of me_key. Note that hash codes are C longs.
* We have to use Py_ssize_t instead because dict_popitem() abuses
* me_hash to hold a search finger.
*/
Py_ssize_t me_hash;
PyObject *me_key;
PyObject *me_value;
} PyDictEntry;

me_hash用于緩存me_key的哈希值,防止每次查詢時都要計算哈希值,entry有三種狀態(tài)。

1.Unused: me_key == me_value == NULL

Unused是entry的初始狀態(tài),key和value都為NULL。插入元素時,Unused狀態(tài)轉(zhuǎn)換成Active狀態(tài)。這是me_key為NULL的唯一情況。

2. Active: me_key != NULL and me_key != dummy 且 me_value != NULL

插入元素后,entry就成了Active狀態(tài),這是me_value唯一不為NULL的情況,刪除元素時Active狀態(tài)刻轉(zhuǎn)換成Dummy狀態(tài)。

3. Dummy: me_key == dummy 且 me_value == NULL

此處的dummy對象實際上一個PyStringObject對象,僅作為指示標志。Dummy狀態(tài)的元素可以在插入元素的時候?qū)⑺兂葾ctive狀態(tài),但它不可能再變成Unused狀態(tài)。

為什么entry有Dummy狀態(tài)呢?這是因為采用開放尋址法中,遇到哈希沖突時會找到下一個合適的位置,例如某元素經(jīng)過哈希計算應(yīng)該插入到A處,但是此時A處有元素的,通過探測函數(shù)計算得到下一個位置B,仍然有元素,直到找到位置C為止,此時ABC構(gòu)成了探測鏈,查找元素時如果hash值相同,那么也是順著這條探測鏈不斷往后找,當刪除探測鏈中的某個元素時,比如B,如果直接把B從哈希表中移除,即變成Unused狀態(tài),那么C就不可能再找到了,因為AC之間出現(xiàn)了斷裂的現(xiàn)象,正是如此才出現(xiàn)了第三種狀態(tài)---Dummy,Dummy是一種類似的偽刪除方式,保證探測鏈的連續(xù)性。

PYDICTOBJECT

PyDictObject就是PyDictEntry對象的集合,PyDictObject的結(jié)構(gòu)是:

typedef struct _dictobject PyDictObject;
struct _dictobject {
PyObject_HEAD
Py_ssize_t ma_fill; /* # Active + # Dummy */
Py_ssize_t ma_used; /* # Active */
/* The table contains ma_mask + 1 slots, and that's a power of 2.
* We store the mask instead of the size because the mask is more
* frequently needed.
*/
Py_ssize_t ma_mask;
/* ma_table points to ma_smalltable for small tables, else to
* additional malloc'ed memory. ma_table is never NULL! This rule
* saves repeated runtime null-tests in the workhorse getitem and
* setitem calls.
*/
PyDictEntry *ma_table;
PyDictEntry *(*ma_lookup)(PyDictObject *mp, PyObject *key, long hash);
PyDictEntry ma_smalltable[PyDict_MINSIZE];
};
  • ma_fill :所有處于Active以及Dummy的元素個數(shù)
  • ma_used :所有處于Active狀態(tài)的元素個數(shù)
  • ma_mask :所有entry的元素個數(shù)(Active+Dummy+Unused)
  • ma_smalltable:創(chuàng)建字典對象時,一定會創(chuàng)建一個大小為PyDict_MINSIZE==8的PyDictEntry數(shù)組。
  • ma_table:當entry數(shù)量小于PyDict_MINSIZE,ma_table指向ma_smalltable的首地址,當entry數(shù)量大于8時,Python把它當做一個大字典來處理,此刻會申請額外的內(nèi)存空間,同時將ma_table指向這塊空間。
  • ma_lookup:字典元素的搜索策略

PyDictObject使用PyObject_HEAD而不是PyObject_Var_HEAD,雖然字典也是變長對象,但此處并不是通過ob_size來存儲字典中元素的長度,而是通過ma_used字段。

PYDICTOBJECT的創(chuàng)建過程

PyObject *
PyDict_New(void)
{
register PyDictObject *mp;
if (dummy == NULL) { /* Auto-initialize dummy */
dummy = PyString_FromString("<dummy key>");
if (dummy == NULL)
return NULL;
}
if (numfree) {
mp = free_list[--numfree];
assert (mp != NULL);
assert (Py_TYPE(mp) == &PyDict_Type);
_Py_NewReference((PyObject *)mp);
if (mp->ma_fill) {
EMPTY_TO_MINSIZE(mp);
} else {
/* At least set ma_table and ma_mask; these are wrong
if an empty but presized dict is added to freelist */
INIT_NONZERO_DICT_SLOTS(mp);
}
assert (mp->ma_used == 0);
assert (mp->ma_table == mp->ma_smalltable);
assert (mp->ma_mask == PyDict_MINSIZE - 1);
} else {
mp = PyObject_GC_New(PyDictObject, &PyDict_Type);
if (mp == NULL)
return NULL;
EMPTY_TO_MINSIZE(mp);
}
mp->ma_lookup = lookdict_string;
return (PyObject *)mp;
}
  • 初始化dummy對象
  • 如果緩沖池還有可用的對象,則從緩沖池中讀取,否則,執(zhí)行步驟3
  • 分配內(nèi)存空間,創(chuàng)建PyDictObject對象,初始化對象
  • 指定添加字典元素時的探測函數(shù),元素的搜索策略

字典搜索策略

static PyDictEntry *
lookdict(PyDictObject *mp, PyObject *key, register long hash)
{
register size_t i;
register size_t perturb;
register PyDictEntry *freeslot;
register size_t mask = (size_t)mp->ma_mask;
PyDictEntry *ep0 = mp->ma_table;
register PyDictEntry *ep;
register int cmp;
PyObject *startkey;

i = (size_t)hash & mask;
ep = &ep0[i];
if (ep->me_key == NULL || ep->me_key == key)
return ep;

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_table && ep->me_key == startkey) {
if (cmp > 0)
return ep;
}
else {
/* The compare did major nasty stuff to the
* dict: start over.
* XXX A clever adversary could prevent this
* XXX from terminating.
*/
return lookdict(mp, key, hash);
}
}
freeslot = NULL;
}

/* In the loop, me_key == dummy is by far (factor of 100s) the
least likely outcome, so test for that last. */
for (perturb = hash; ; perturb >>= PERTURB_SHIFT) {
i = (i << 2) + i + perturb + 1;
ep = &ep0[i & mask];
if (ep->me_key == NULL)
return freeslot == NULL ? ep : freeslot;
if (ep->me_key == key)
return ep;
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)
return NULL;
if (ep0 == mp->ma_table && ep->me_key == startkey) {
if (cmp > 0)
return ep;
}
else {
/* The compare did major nasty stuff to the
* dict: start over.
* XXX A clever adversary could prevent this
* XXX from terminating.
*/
return lookdict(mp, key, hash);
}
}
else if (ep->me_key == dummy && freeslot == NULL)
freeslot = ep;
}
assert(0); /* NOT REACHED */
return 0;
}

字典在添加元素和查詢元素時,都需要用到字典的搜索策略,搜索時,如果不存在該key,那么返回Unused狀態(tài)的entry,如果存在該key,但是key是一個Dummy對象,那么返回Dummy狀態(tài)的entry,其他情況就表示存在Active狀態(tài)的entry,那么對于字典的插入操作,針對不同的情況進行操作也不一樣。對于Active的entry,直接替換me_value值即可;對于Unused或Dummy的entry,需要同時設(shè)置me_key,me_hash和me_value

PYDICTOBJECT對象緩沖池

PyDictObject對象緩沖池和PyListObject對象緩沖池的原理是類似的,都是在對象被銷毀的時候把該對象添加到緩沖池中去,而且值保留PyDictObject對象本身,如果ma_table維護的時從系統(tǒng)堆中申請的空間,那么Python會釋放這塊內(nèi)存,如果ma_table維護的是ma_smalltable,那么只需把smalltable中的元素的引用計數(shù)減少即可。

static void
dict_dealloc(register PyDictObject *mp)
{
register PyDictEntry *ep;
Py_ssize_t fill = mp->ma_fill;
PyObject_GC_UnTrack(mp);
Py_TRASHCAN_SAFE_BEGIN(mp)
for (ep = mp->ma_table; fill > 0; ep++) {
if (ep->me_key) {
--fill;
Py_DECREF(ep->me_key);
Py_XDECREF(ep->me_value);
}
}
if (mp->ma_table != mp->ma_smalltable)
PyMem_DEL(mp->ma_table);
if (numfree < PyDict_MAXFREELIST && Py_TYPE(mp) == &PyDict_Type)
free_list[numfree++] = mp;
else
Py_TYPE(mp)->tp_free((PyObject *)mp);
Py_TRASHCAN_SAFE_END(mp)
}

以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • Django-Rest-Framework 權(quán)限管理源碼淺析(小結(jié))

    Django-Rest-Framework 權(quán)限管理源碼淺析(小結(jié))

    這篇文章主要介紹了Django-Rest-Framework 權(quán)限管理源碼淺析(小結(jié)),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-11-11
  • Python中的八大核心語句你知道幾個呢?

    Python中的八大核心語句你知道幾個呢?

    Python?是一種代表簡單思想的語言,其語法相對簡單,很容易上手。本文精心篩選了Python中的八大核心語句,快來看看你都掌握了幾個呢
    2023-02-02
  • python中統(tǒng)計相同字符的個數(shù)方法實例

    python中統(tǒng)計相同字符的個數(shù)方法實例

    我們在開發(fā)中經(jīng)常需要統(tǒng)計某個字符或字符串出現(xiàn)的次數(shù),下面這篇文章主要給大家介紹了關(guān)于python中統(tǒng)計相同字符的個數(shù)的相關(guān)資料,文中通過實例代碼介紹的非常詳細,需要的朋友可以參考下
    2023-01-01
  • python ubplot使用方法解析

    python ubplot使用方法解析

    這篇文章主要介紹了python ubplot使用方法解析,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-01-01
  • Django框架中的對象列表視圖使用示例

    Django框架中的對象列表視圖使用示例

    這篇文章主要介紹了Django框架中的對象列表視圖使用示例,Django是重多Python人氣web框架中最為著名的一個,需要的朋友可以參考下
    2015-07-07
  • python爬取企查查企業(yè)信息之selenium自動模擬登錄企查查

    python爬取企查查企業(yè)信息之selenium自動模擬登錄企查查

    這篇文章主要介紹了python爬取企查查企業(yè)信息之自動模擬登錄企查查以及selenium獲取headers,selenium獲取cookie,需要的朋友可以參考下
    2021-04-04
  • 基于Pydantic封裝的通用模型在API請求驗證中的應(yīng)用詳解

    基于Pydantic封裝的通用模型在API請求驗證中的應(yīng)用詳解

    這篇文章主要介紹了基于Pydantic封裝的通用模型在API請求驗證中的應(yīng)用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步早日升職加薪
    2023-05-05
  • Python webdriver.Chrome()的使用解讀

    Python webdriver.Chrome()的使用解讀

    這篇文章主要介紹了Python webdriver.Chrome()的使用,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-02-02
  • Python讀取圖片屬性信息的實現(xiàn)方法

    Python讀取圖片屬性信息的實現(xiàn)方法

    這篇文章介紹了利用Python讀取圖片屬性信息的方法,讀取的內(nèi)容包括GPS 信息、圖片分辨率、圖片像素、設(shè)備商、拍攝設(shè)備等,有需要的朋友們可以參考借鑒。
    2016-09-09
  • 使用Python繪制動態(tài)愛心并表白的代碼詳解

    使用Python繪制動態(tài)愛心并表白的代碼詳解

    在這個充滿浪漫的季節(jié),如何用代碼表達你的愛意呢?今天我們將使用 Python 的 matplotlib 和 numpy 庫繪制一個動態(tài)的愛心,并且在愛心上添加表白的文字,這將是一個獨特而浪漫的方式來表達你的心聲,感興趣的小伙伴跟著小編來看看吧
    2025-04-04

最新評論

揭西县| 抚宁县| 英山县| 汕头市| 石家庄市| 仲巴县| 囊谦县| 夏津县| 阿尔山市| 昭通市| 当涂县| 兰州市| 定日县| 崇义县| 莱芜市| 泾阳县| 尤溪县| 南康市| 马边| 资阳市| 莆田市| 临洮县| 天长市| 定西市| 桂东县| 洪洞县| 宝清县| 泰顺县| 红原县| 根河市| 阿拉善右旗| 沿河| 卢湾区| 信丰县| 宝山区| 常山县| 黎城县| 揭东县| 崇州市| 沂南县| 建始县|