日韩成人免费在线_国产成人一二_精品国产免费人成电影在线观..._日本一区二区三区久久久久久久久不

當(dāng)前位置:首頁 > 科技  > 軟件

解密 Python 集合的實(shí)現(xiàn)原理

來源: 責(zé)編: 時(shí)間:2024-09-10 09:50:43 174觀看
導(dǎo)讀楔子本篇文章來聊一聊 Python 的集合是怎么實(shí)現(xiàn)的?前面我們介紹了字典的實(shí)現(xiàn)原理,它底層是基于哈希表實(shí)現(xiàn)的,而集合也是如此。事實(shí)上,集合就類似于沒有 value 的字典。集合的使用場(chǎng)景那么集合都有哪些用處呢?1)去重chars =

aZJ28資訊網(wǎng)——每日最新資訊28at.com

楔子

aZJ28資訊網(wǎng)——每日最新資訊28at.com

本篇文章來聊一聊 Python 的集合是怎么實(shí)現(xiàn)的?前面我們介紹了字典的實(shí)現(xiàn)原理,它底層是基于哈希表實(shí)現(xiàn)的,而集合也是如此。aZJ28資訊網(wǎng)——每日最新資訊28at.com

事實(shí)上,集合就類似于沒有 value 的字典。aZJ28資訊網(wǎng)——每日最新資訊28at.com

aZJ28資訊網(wǎng)——每日最新資訊28at.com

集合的使用場(chǎng)景

aZJ28資訊網(wǎng)——每日最新資訊28at.com

那么集合都有哪些用處呢?aZJ28資訊網(wǎng)——每日最新資訊28at.com

1)去重

chars = ["a", "b", "a", "c", "c"]print(    list(set(chars)))  # ['b', 'a', 'c']

比如你需要監(jiān)聽一個(gè)隊(duì)列,處理接收到的消息,但每一條消息都有一個(gè)編號(hào),要保證具有相同編號(hào)的消息只能被處理一次,要怎么做呢?aZJ28資訊網(wǎng)——每日最新資訊28at.com

顯然集合此時(shí)就派上用場(chǎng)了,我們可以創(chuàng)建一個(gè)集合,每來一條消息,就檢測(cè)它的編號(hào)是否在集合中。如果存在,則說明消息已經(jīng)被處理過了,忽略掉;如果不存在,說明消息還沒有被處理,那么就將它的編號(hào)添加到集合中,然后處理消息。aZJ28資訊網(wǎng)——每日最新資訊28at.com

2)判斷某個(gè)序列是否包含指定的多個(gè)元素

data = ["S", "A", "T", "O", "R", "I"]# 現(xiàn)在要判斷 data 是否包含 "T"、"R" 和 "I"# 如果使用列表的話print(    "T" in data and "R" in data and "I" in data)  # True# 顯然使用列表比較麻煩,并且效率也不高,于是我們可以使用集合print(    set(data) >= {"T", "R", "I"})  # True

同理,基于此方式,我們也可以檢測(cè)一個(gè)字典是否包含指定的多個(gè) key。aZJ28資訊網(wǎng)——每日最新資訊28at.com

data = {    "name": "satori",    "age": 17,    "gender": "female"}# 判斷字典是否包含 name、age、gender 三個(gè) keyprint(    data.keys() >= {"name", "age", "gender"})  # True# 字典的 keys 方法會(huì)返回一個(gè) dict_keys 對(duì)象# 該對(duì)象具備集合的性質(zhì),可以直接和集合進(jìn)行運(yùn)算

顯然對(duì)于這種需求,有了集合就方便多了。aZJ28資訊網(wǎng)——每日最新資訊28at.com

aZJ28資訊網(wǎng)——每日最新資訊28at.com

集合的 API

aZJ28資訊網(wǎng)——每日最新資訊28at.com

然后我們來羅列一下集合支持的 API,在使用集合的時(shí)候要做到心中有數(shù)。aZJ28資訊網(wǎng)——每日最新資訊28at.com

# 如果是創(chuàng)建一個(gè)空集合,那么要使用 set()# 寫成 {} 的話,解釋器會(huì)認(rèn)為這是一個(gè)空字典s = {1, 2, 3}# 添加元素,時(shí)間復(fù)雜度是 O(1)s.add(4)print(s)  # {1, 2, 3, 4}# 刪除指定的元素,如果元素不存在,會(huì)拋出 KeyError# 時(shí)間復(fù)雜度為 O(1)s.remove(2)print(s)  # {1, 3, 4}# 刪除指定的元素,如果元素不存在則什么也不做# 時(shí)間復(fù)雜度為 O(1)s.discard(666)print(s)  # {1, 3, 4}# 隨機(jī)彈出一個(gè)元素并返回,如果集合為空,會(huì)拋出 KeyError# 時(shí)間復(fù)雜度為 O(1)print(s.pop())  # 1print(s)  # {3, 4}# 清空一個(gè)集合s.clear()print(s)  # set()# 還有一些 API,但我們更推薦使用操作符的方式# 兩個(gè)集合取交集print({1, 2} & {2, 3})  # {2}# 兩個(gè)集合取并集print({1, 2} | {2, 3})  # {1, 2, 3}# 兩個(gè)集合取差集# s1 - s2,返回在 s1、但不在 s2 當(dāng)中的元素print({1, 2, 3} - {2, 3, 4})  # {1}# 兩個(gè)集合取對(duì)稱差集# s1 ^ s2,返回既不在 s1、也不在 s2 當(dāng)中的元素print({1, 2, 3} ^ {2, 3, 4})  # {1, 4}# 判斷兩個(gè)集合是否相等,也就是內(nèi)部的元素是否完全一致# 順序無所謂,只比較元素是否全部相同print({1, 2, 3} == {3, 2, 1})  # Trueprint({1, 2, 3} == {1, 2, 4})  # False# 判斷一個(gè)集合是否包含另一個(gè)集合的所有元素# 假設(shè)有兩個(gè)集合 s1 和 s2:#    如果 s1 的元素都在 s2 中,那么 s2 >= s1;#    如果 s2 的元素都在 s1 中,那么 s1 >= s2;#    如果 s1 和元素和 s2 全部相同,那么 s1 == s2;print({1, 2, 3} > {1, 2})  # Trueprint({1, 2, 3} >= {1, 2, 3})  # True

以上就是集合支持的一些 API,還是很簡(jiǎn)單的。aZJ28資訊網(wǎng)——每日最新資訊28at.com

aZJ28資訊網(wǎng)——每日最新資訊28at.com

集合的底層結(jié)構(gòu)

aZJ28資訊網(wǎng)——每日最新資訊28at.com

集合和字典的內(nèi)部都使用了哈希表,但字典的哈希表采用兩個(gè)數(shù)組實(shí)現(xiàn),而集合的哈希表采用一個(gè)數(shù)組實(shí)現(xiàn)。因此對(duì)于集合來說,這個(gè)數(shù)組不僅要存儲(chǔ) entry,并且映射出的索引也是該數(shù)組的索引。aZJ28資訊網(wǎng)——每日最新資訊28at.com

下面看一下集合的底層結(jié)構(gòu)長(zhǎng)什么樣子。aZJ28資訊網(wǎng)——每日最新資訊28at.com

// Include/cpython/setobject.htypedef struct {    PyObject_HEAD    Py_ssize_t fill;      Py_ssize_t used;          Py_ssize_t mask;    setentry *table;    Py_hash_t hash;              Py_ssize_t finger;        setentry smalltable[PySet_MINSIZE];    PyObject *weakreflist;     } PySetObject;

解釋一下這些字段的含義:aZJ28資訊網(wǎng)——每日最新資訊28at.com

PyObject_HEAD

定長(zhǎng)對(duì)象的頭部信息,但集合顯然是一個(gè)變長(zhǎng)對(duì)象,所以和字典一樣,肯定有其它字段充當(dāng) ob_size。aZJ28資訊網(wǎng)——每日最新資訊28at.com

Py_ssize_t fill

Active 態(tài)的 entry 數(shù)量加上 Dummy 態(tài)的 entry 數(shù)量。一個(gè) entry 就是哈希表里的一個(gè)元素,類型為 setentry,因此在集合里面,一個(gè) entry 就是一個(gè) setentry 結(jié)構(gòu)體實(shí)例。當(dāng)刪除集合的 entry 時(shí),也必須是偽刪除,因?yàn)橐WC探測(cè)鏈不斷裂。如果 entry 被偽刪除了,那么它便處于 Dummy 態(tài)。aZJ28資訊網(wǎng)——每日最新資訊28at.com

Py_ssize_t used

Active 態(tài)的 entry 數(shù)量,顯然這個(gè) used 充當(dāng)了 ob_size,也就是集合的元素個(gè)數(shù);aZJ28資訊網(wǎng)——每日最新資訊28at.com

Py_ssize_t mask

在看字典源碼的時(shí)候,我們也見到了 mask,它用于和哈希值進(jìn)行按位與、計(jì)算索引,并且這個(gè) mask 等于哈希表的容量減 1,為什么呢?假設(shè)哈希值等于 v,哈希表容量是 n,那么通過 v 對(duì) n 取模即可得到一個(gè)位于 0 到 n-1 之間的數(shù)。但是取模運(yùn)算的效率不高,而 v&(n-1) 的作用等價(jià)于 v%n,并且速度更快,所以 mask 的值要等于哈希表的容量減 1。但是注意,只有在 n 為 2 的冪次方的時(shí)候,v&(n-1) 和 v%n 才是完全等價(jià)的,所以哈希表的容量要求是 2 的冪次方,就是為了將取模運(yùn)算優(yōu)化成按位與運(yùn)算。aZJ28資訊網(wǎng)——每日最新資訊28at.com

setentry *table

指向 setentry 數(shù)組首元素的指針,這個(gè) setentry 數(shù)組可以是下面的 smalltable,也可以是單獨(dú)申請(qǐng)的一塊內(nèi)存;aZJ28資訊網(wǎng)——每日最新資訊28at.com

Py_hash_t hash

集合的哈希值,只適用于不可變集合;aZJ28資訊網(wǎng)——每日最新資訊28at.com

Py_ssize_t finger

用于 pop 方法;aZJ28資訊網(wǎng)——每日最新資訊28at.com

setentry smalltable[8]

一個(gè) setentry 類型的數(shù)組,集合的元素就存在里面。但我們知道,變長(zhǎng)對(duì)象的內(nèi)部不會(huì)存儲(chǔ)具體的元素,而是會(huì)存儲(chǔ)一個(gè)指針,該指針指向的內(nèi)存區(qū)域才是用來存儲(chǔ)具體元素的。這樣當(dāng)擴(kuò)容的時(shí)候,只需要讓指針指向新的內(nèi)存區(qū)域即可,從而方便維護(hù)。沒錯(cuò),對(duì)于集合而言,只有在容量不超過 8 的時(shí)候,元素才會(huì)存在里面;而一旦超過了 8,那么會(huì)使用 malloc 單獨(dú)申請(qǐng)內(nèi)存;aZJ28資訊網(wǎng)——每日最新資訊28at.com

weakreflist

弱引用列表,不做深入討論;aZJ28資訊網(wǎng)——每日最新資訊28at.com

有了字典的經(jīng)驗(yàn),再看集合會(huì)簡(jiǎn)單很多。然后是 setentry,用于承載集合內(nèi)的元素,那么它的結(jié)構(gòu)長(zhǎng)什么樣呢?相信你能夠猜到。aZJ28資訊網(wǎng)——每日最新資訊28at.com

// Include/cpython/setobject.h#define PySet_MINSIZE 8typedef struct {    PyObject *key;    Py_hash_t hash;            } setentry;

相比字典少了一個(gè) value,這是顯而易見的。aZJ28資訊網(wǎng)——每日最新資訊28at.com

因此集合的結(jié)構(gòu)很清晰了,假設(shè)有一個(gè)集合 {3.14, "abc", 666},那么它的結(jié)構(gòu)如下:aZJ28資訊網(wǎng)——每日最新資訊28at.com

圖片圖片aZJ28資訊網(wǎng)——每日最新資訊28at.com

由于集合里面只有三個(gè)元素,所以它們都會(huì)存在 smalltable 數(shù)組里面,我們通過 ctypes 來證明這一點(diǎn)。aZJ28資訊網(wǎng)——每日最新資訊28at.com

from ctypes import *class PyObject(Structure):    _fields_ = [        ("ob_refcnt", c_ssize_t),        ("ob_type", c_void_p),    ]class SetEntry(Structure):    _fields_ = [        ("key", POINTER(PyObject)),        ("hash", c_longlong)    ]class PySetObject(PyObject):    _fields_ = [        ("fill", c_ssize_t),        ("used", c_ssize_t),        ("mask", c_ssize_t),        ("table", POINTER(SetEntry)),        ("hash", c_long),        ("finger", c_ssize_t),        ("smalltable", (SetEntry * 8)),        ("weakreflist", POINTER(PyObject)),    ]s = {3.14, "abc", 666}# 先來打印一下哈希值print('hash(3.14) =', hash(3.14))print('hash("abc") =', hash("abc"))print('hash(666) =', hash(666))"""hash(3.14) = 322818021289917443hash("abc") = 8036038346376407734hash(666) = 666"""# 獲取 PySetObject 結(jié)構(gòu)體實(shí)例py_set_obj = PySetObject.from_address(id(s))# 遍歷 smalltable,打印索引和 key 的哈希值for index, entry in enumerate(py_set_obj.smalltable):    print(index, entry.hash)"""0 01 02 6663 3228180212899174434 05 06 80360383463764077347 0"""

根據(jù)輸出的哈希值我們可以斷定,這三個(gè)元素確實(shí)存在了 smalltable 數(shù)組里面,并且 666 存在了數(shù)組索引為 2 的位置、3.14 存在了數(shù)組索引為 3 的位置、"abc" 存在了數(shù)組索引為 6 的位置。aZJ28資訊網(wǎng)——每日最新資訊28at.com

當(dāng)然,由于哈希值是隨機(jī)的,所以每次執(zhí)行之后打印的結(jié)果都可能不一樣,但是整數(shù)除外,它的哈希值就是它本身。既然哈希值不一樣,那么每次映射出的索引也可能不同,但總之這三個(gè)元素是存在 smalltable 數(shù)組里面的。aZJ28資訊網(wǎng)——每日最新資訊28at.com

然后我們?cè)倏疾煲幌缕渌淖侄危?span style="display:none">aZJ28資訊網(wǎng)——每日最新資訊28at.com

s = {3.14, "abc", 666}py_set_obj = PySetObject.from_address(id(s))# 集合里面有 3 個(gè)元素,所以 fill 和 used 都是 3print(py_set_obj.fill)  # 3print(py_set_obj.used)  # 3# 將集合元素全部刪除# 這里不能用 s.clear(),原因一會(huì)兒說for _ in range(len(s)):    s.pop()    # 我們知道哈希表在刪除元素的時(shí)候是偽刪除# 所以 fill 不變,但是 used 每次會(huì)減 1print(py_set_obj.fill)  # 3print(py_set_obj.used)  # 0

fill 字段維護(hù)的是 Active 態(tài)的 entry 數(shù)量加上 Dummy 態(tài)的 entry 數(shù)量,所以刪除元素時(shí)它的大小是不變的。但 used 字段的值每次會(huì)減 1,因?yàn)樗S護(hù)的是 Active 態(tài)的 entry 的數(shù)量。所以在不涉及元素的刪除時(shí),這兩者的大小是相等的。aZJ28資訊網(wǎng)——每日最新資訊28at.com

另外我們說上面不能用 s.clear(),因?yàn)樵摲椒ū硎厩蹇占?,此時(shí)會(huì)重置為初始狀態(tài),然后 fill 和 used 都會(huì)是 0,這樣就觀察不到想要的現(xiàn)象了。aZJ28資訊網(wǎng)——每日最新資訊28at.com

刪除集合所有元素之后,我們?cè)偻锩嫣砑釉?,看看是什么效果?span style="display:none">aZJ28資訊網(wǎng)——每日最新資訊28at.com

s = {3.14, "abc", 666}py_set_obj = PySetObject.from_address(id(s))for _ in range(len(s)):    s.pop()# 添加一個(gè)元素s.add(0)print(py_set_obj.fill)  # 3print(py_set_obj.used)  # 1

多次執(zhí)行的話,會(huì)發(fā)現(xiàn)打印的結(jié)果可能是 3、1,也有可能是 4、1。至于原因,有了字典的經(jīng)驗(yàn),相信你肯定能猜到。aZJ28資訊網(wǎng)——每日最新資訊28at.com

首先添加元素之后,used 肯定為 1。至于 fill,如果添加元素的時(shí)候,正好撞上了一個(gè) Dummy 態(tài)的 entry,那么將其替換掉,此時(shí) fill 不變,仍然是 3。但如果沒有撞上 Dummy 態(tài)的 entry,而是添加在了新的位置,那么 fill 就是 4。aZJ28資訊網(wǎng)——每日最新資訊28at.com

for i in range(1, 10):    s.add(i)print(py_set_obj.fill)  # 10print(py_set_obj.used)  # 10s.pop()print(py_set_obj.fill)  # 10print(py_set_obj.used)  # 9

在之前代碼的基礎(chǔ)上,繼續(xù)添加 9 個(gè)元素,然后 used 變成了 10,這很好理解,因?yàn)榇藭r(shí)集合有 10 個(gè)元素。但 fill 也是 10,這是為什么?很簡(jiǎn)單,因?yàn)楣1頂U(kuò)容了,擴(kuò)容時(shí)會(huì)刪除 Dummy 態(tài)的 entry,所以 fill 和 used 是相等的。同理,如果再繼續(xù) pop,那么 fill 和 used 就又變得不相等了。aZJ28資訊網(wǎng)——每日最新資訊28at.com

aZJ28資訊網(wǎng)——每日最新資訊28at.com

aZJ28資訊網(wǎng)——每日最新資訊28at.com

aZJ28資訊網(wǎng)——每日最新資訊28at.com

集合的創(chuàng)建

aZJ28資訊網(wǎng)——每日最新資訊28at.com

集合的結(jié)構(gòu)我們已經(jīng)清楚了,再來看看它的初始化過程。我們調(diào)用類 set,傳入一個(gè)可迭代對(duì)象,便可創(chuàng)建一個(gè)集合,這個(gè)過程是怎樣的呢?aZJ28資訊網(wǎng)——每日最新資訊28at.com

// Objects/setobject.cPyObject *PySet_New(PyObject *iterable){    return make_new_set(&PySet_Type, iterable);}static PyObject *make_new_set(PyTypeObject *type, PyObject *iterable){    assert(PyType_Check(type));    PySetObject *so;    // 為 PySetObject 申請(qǐng)內(nèi)存,初始容量為 8    so = (PySetObject *)type->tp_alloc(type, 0);    if (so == NULL)        return NULL;    // 對(duì)字段做初始化    so->fill = 0;    so->used = 0;    so->mask = PySet_MINSIZE - 1;    // 哈希表容量為 8 時(shí),元素會(huì)存在 smalltable 里面    // 因此直接將 smalltable 賦值給 table    so->table = so->smalltable;    so->hash = -1;    so->finger = 0;    so->weakreflist = NULL;    if (iterable != NULL) {        // 遍歷 iterable,將迭代出的元素添加到集合中        // 關(guān)于這個(gè)函數(shù),我們之后再介紹        if (set_update_internal(so, iterable)) {            Py_DECREF(so);            return NULL;        }    }    return (PyObject *)so;}

可以看到,集合的創(chuàng)建過程非常簡(jiǎn)單。aZJ28資訊網(wǎng)——每日最新資訊28at.com

aZJ28資訊網(wǎng)——每日最新資訊28at.com

字典和集合的哈希表的差異

aZJ28資訊網(wǎng)——每日最新資訊28at.com

字典和集合都是采用哈希表實(shí)現(xiàn)的,但字典的哈希表使用了兩個(gè)數(shù)組,而集合的哈希表使用了一個(gè)數(shù)組,我們對(duì)比一下兩者的差異。aZJ28資訊網(wǎng)——每日最新資訊28at.com

假設(shè)有一個(gè)字典和一個(gè)集合,字典包含三個(gè)鍵值對(duì),分別是 "a": 1、"b": 2、"c": 3,集合包含三個(gè)元素,分別是 "a"、"b"、"c",然后映射出的索引分別是 2、5、3。aZJ28資訊網(wǎng)——每日最新資訊28at.com

圖片圖片aZJ28資訊網(wǎng)——每日最新資訊28at.com

注:為了方便,這里的圖畫得沒有那么嚴(yán)謹(jǐn)。比如集合的哈希表,里面的元素直接用字符串代替了,但其實(shí)它存儲(chǔ)的是 setentry entry,而 entry 的 key 字段指向的才是字符串。當(dāng)然這里我們心里清楚就好。aZJ28資訊網(wǎng)——每日最新資訊28at.com

在介紹字典的時(shí)候我們說過,早期的字典內(nèi)部的哈希表也是使用一個(gè)數(shù)組實(shí)現(xiàn),除了 entry 會(huì)多存儲(chǔ)一個(gè) value 之外,其它和當(dāng)前的集合是類似的。aZJ28資訊網(wǎng)——每日最新資訊28at.com

但如果只使用一個(gè)數(shù)組實(shí)現(xiàn),會(huì)導(dǎo)致內(nèi)存浪費(fèi)嚴(yán)重,因?yàn)楣1肀仨氁WC一定的稀疏性。所以后續(xù)字典內(nèi)部的哈希表采用兩個(gè)數(shù)組實(shí)現(xiàn),將存儲(chǔ)鍵值對(duì)的數(shù)組的長(zhǎng)度壓縮到原來的 2/3,至于映射出的索引則由另一個(gè)數(shù)組(哈希索引數(shù)組)來承載。aZJ28資訊網(wǎng)——每日最新資訊28at.com

雖然引入新的數(shù)組會(huì)帶來額外的內(nèi)存開銷(假設(shè)大小為 m 字節(jié)),但存儲(chǔ)鍵值對(duì)的數(shù)組不用再浪費(fèi) 1/3 的空間(假設(shè)大小為 n 字節(jié)),只要 m 小于 n,那么使用兩個(gè)數(shù)組就會(huì)更加節(jié)省內(nèi)存。而在介紹字典的時(shí)候我們也看到了,m 是遠(yuǎn)小于 n 的。aZJ28資訊網(wǎng)——每日最新資訊28at.com

那么問題來了,為什么集合不使用兩個(gè)數(shù)組呢?很簡(jiǎn)單,因?yàn)槭褂靡粋€(gè)數(shù)組實(shí)現(xiàn)哈希表會(huì)更簡(jiǎn)單,雖然也更加浪費(fèi)內(nèi)存。而集合和字典在哈希表的實(shí)現(xiàn)上之所以區(qū)別對(duì)待,還是使用頻率的問題,解釋器內(nèi)部極度依賴字典,比如全局變量就是使用字典存儲(chǔ)的。aZJ28資訊網(wǎng)——每日最新資訊28at.com

可以說字典的效率高度影響著整個(gè)解釋器的效率,字典的內(nèi)存大小高度影響著解釋器的內(nèi)存占用。因此 Python 除了優(yōu)化字典的搜索性能之外,還要盡可能地減少字典的內(nèi)存大小。所以字典搞出了分離表、結(jié)合表,以及根據(jù) key 是否全部是字符串來選擇使用不同的結(jié)構(gòu)體表示 entry,這一切操作都是為了將字典的內(nèi)存占用降到最低。aZJ28資訊網(wǎng)——每日最新資訊28at.com

至于集合,解釋器對(duì)它的依賴就很小了,所以內(nèi)部的哈希表,只采用了一個(gè)數(shù)組實(shí)現(xiàn)。雖然會(huì)有內(nèi)存浪費(fèi),但無傷大雅。aZJ28資訊網(wǎng)——每日最新資訊28at.com

好,回到上面的例子,如果將字典的鍵值對(duì) "b": 2 和集合的元素 "b" 刪掉,那么它們的結(jié)構(gòu)會(huì)發(fā)生什么變化呢?aZJ28資訊網(wǎng)——每日最新資訊28at.com

圖片圖片aZJ28資訊網(wǎng)——每日最新資訊28at.com

"b" 映射出的索引為 5,因此對(duì)于字典來說,會(huì)將索引為 5 的哈希槽的值設(shè)置為 dummy。然后是鍵值對(duì)數(shù)組,會(huì)將指定的 entry 的 me_key 和 me_value 字段全部設(shè)置為 NULL,相當(dāng)于回歸到了初始狀態(tài)。aZJ28資訊網(wǎng)——每日最新資訊28at.com

需要注意的是,數(shù)組一旦申請(qǐng),那么 entry 的空間就已經(jīng)有了,只是 me_key 和 me_value 字段均為 NULL。而所謂添加鍵值對(duì),本質(zhì)上也是修改指定 entry 的 me_key 和 me_value 字段。aZJ28資訊網(wǎng)——每日最新資訊28at.com

對(duì)于集合來說,它只有一個(gè)數(shù)組,這個(gè)數(shù)組不僅要存儲(chǔ)鍵值對(duì),它的索引還表示 key 映射出的索引,當(dāng)然這里的 key 指的就是集合的元素。"b" 映射出的索引為 5,所以將數(shù)組中索引為 5 的 entry->key 設(shè)置為 dummy。aZJ28資訊網(wǎng)——每日最新資訊28at.com

但要注意的是,字典的 dummy 是一個(gè)整數(shù),值為 -2(DKIX_DUMMY),因?yàn)楣K饕龜?shù)組存儲(chǔ)的是整數(shù)。key 映射出的索引是哈希索引數(shù)組的索引,如果對(duì)應(yīng)的哈希槽存儲(chǔ)的值是 -2,說明當(dāng)前搜索的 key 對(duì)應(yīng)的 entry 被刪除了,應(yīng)該繼續(xù)向后搜索。aZJ28資訊網(wǎng)——每日最新資訊28at.com

而集合的 dummy 是一個(gè)結(jié)構(gòu)體指針,定義如下:aZJ28資訊網(wǎng)——每日最新資訊28at.com

// Objects/setobject.cstatic PyObject _dummy_struct;#define dummy (&_dummy_struct)

因?yàn)榧蟽?nèi)部的哈希表只使用了一個(gè)數(shù)組,該數(shù)組存儲(chǔ)的是 setentry。如果在查找的時(shí)候,發(fā)現(xiàn)對(duì)應(yīng)的 entry 的 key 等于 dummy,就知道該 entry 被刪除了,應(yīng)該繼續(xù)向后搜索。aZJ28資訊網(wǎng)——每日最新資訊28at.com

好,繼續(xù)回到上面的例子,假設(shè)這時(shí)候再給字典添加一個(gè)鍵值對(duì) "d": 4,給集合添加一個(gè)元素 "d",而字符串 "d" 映射出的索引也是 5,那么結(jié)構(gòu)是怎樣的呢?aZJ28資訊網(wǎng)——每日最新資訊28at.com

圖片圖片aZJ28資訊網(wǎng)——每日最新資訊28at.com

對(duì)于字典來說,鍵值對(duì)始終按照先來后到的順序添加在鍵值對(duì)數(shù)組中,然后將它在鍵值對(duì)數(shù)組中的索引保存在指定的哈希槽中。由于索引為 5 的哈希槽保存的是 -2,處于 Dummy 態(tài),因此直接將它設(shè)置為 3。aZJ28資訊網(wǎng)——每日最新資訊28at.com

同理對(duì)于集合來說也是類似的。數(shù)組索引為 5 的位置保存的值等于 dummy,處于 Dummy 態(tài),說明該元素被刪除了,那么直接替換掉。因此整個(gè)過程的邏輯很簡(jiǎn)單:由于索引會(huì)存在沖突,所以元素刪除之后,需要寫入一個(gè)特殊的墓碑值,也就是這里的 dummy,因?yàn)橐WC探測(cè)鏈不斷裂。但如果集合后續(xù)添加元素時(shí),正好撞上了一個(gè) Dummy 態(tài)的 entry,那么會(huì)直接替換掉。aZJ28資訊網(wǎng)——每日最新資訊28at.com

所以不論是字典還是集合,只要處于 Dummy 態(tài),都可以替換掉。因?yàn)?Dummy 態(tài)存在的目的就是為了保證探測(cè)鏈不斷裂,而替換之后探測(cè)鏈依舊是完整的。aZJ28資訊網(wǎng)——每日最新資訊28at.com

aZJ28資訊網(wǎng)——每日最新資訊28at.com

小結(jié)

aZJ28資訊網(wǎng)——每日最新資訊28at.com

以上我們就剖析了集合的底層結(jié)構(gòu)以及它的創(chuàng)建過程,不難發(fā)現(xiàn)集合的實(shí)現(xiàn)比字典要簡(jiǎn)單很多,并且集合沒有自己的緩存池。aZJ28資訊網(wǎng)——每日最新資訊28at.com

本文鏈接:http://m.www897cc.com/showinfo-26-112773-0.html解密 Python 集合的實(shí)現(xiàn)原理

聲明:本網(wǎng)頁內(nèi)容旨在傳播知識(shí),若有侵權(quán)等問題請(qǐng)及時(shí)與本網(wǎng)聯(lián)系,我們將在第一時(shí)間刪除處理。郵件:2376512515@qq.com

上一篇: 如何利用CSS實(shí)現(xiàn)三角形、扇形、聊天氣泡框

下一篇: 好的代碼重構(gòu) vs 壞的代碼重構(gòu):如何做出正確選擇?

標(biāo)簽:
  • 熱門焦點(diǎn)
Top 日韩成人免费在线_国产成人一二_精品国产免费人成电影在线观..._日本一区二区三区久久久久久久久不
一区二区三区在线免费观看| 美日韩在线观看| 日韩特黄影片| 亚洲手机视频| 欧美一区二粉嫩精品国产一线天| 久久国产精品高清| 欧美不卡激情三级在线观看| 欧美日韩一区三区| 国产一区二区毛片| 亚洲激情成人在线| 亚洲一区亚洲二区| 久久亚洲色图| 国产精品a久久久久久| 国模叶桐国产精品一区| aa级大片欧美三级| 欧美呦呦网站| 欧美精品v日韩精品v韩国精品v | 久久亚洲不卡| 欧美日韩午夜| 狠狠色综合网站久久久久久久| 亚洲精品国产精品国产自| 先锋影音一区二区三区| 欧美激情一区二区三区四区| 国产欧美精品va在线观看| 亚洲国产精品va在线看黑人| 亚洲免费人成在线视频观看| 麻豆成人在线播放| 国产精品尤物| 日韩一区二区高清| 久久最新视频| 国产日韩欧美自拍| 一区二区日韩伦理片| 久久久久久久综合日本| 国产精品毛片| 亚洲精品美女久久7777777| 欧美在线观看网址综合| 欧美日韩在线观看一区二区三区| 尤物yw午夜国产精品视频| 中文成人激情娱乐网| 欧美α欧美αv大片| 国产亚洲精品一区二555| 99精品视频网| 老司机精品导航| 国产欧美一级| 亚洲图片欧洲图片av| 欧美国产日韩亚洲一区| 黄色成人片子| 先锋资源久久| 国产精品播放| 亚洲免费高清视频| 美女久久一区| 国产在线拍揄自揄视频不卡99| 亚洲视频免费在线| 欧美精品一区二区久久婷婷| 在线播放国产一区中文字幕剧情欧美| 先锋影音网一区二区| 国产精品免费视频观看| 宅男噜噜噜66一区二区66| 欧美第一黄网免费网站| 在线观看视频亚洲| 久久久久综合一区二区三区| 国产亚洲精品成人av久久ww| 午夜欧美不卡精品aaaaa| 国产精品mm| 一区二区三区国产精华| 欧美日本国产在线| 9色精品在线| 欧美日韩免费观看一区| 日韩视频不卡中文| 欧美精品免费在线| 亚洲精品视频在线看| 欧美二区在线播放| 亚洲人成精品久久久久| 欧美大片在线观看一区| 亚洲国产欧美在线人成| 欧美~级网站不卡| 91久久精品国产91性色tv| 欧美h视频在线| 亚洲区欧美区| 欧美理论电影在线播放| 日韩一级大片| 欧美视频在线一区| 亚洲一区免费| 国产精品一区在线观看| 欧美一区二区三区在线观看| 国产一区二区精品丝袜| 久久久久久欧美| 亚洲成人影音| 欧美激情一区二区三区在线| 亚洲免费黄色| 欧美亚洲第一页| 午夜精品成人在线视频| 国产亚洲va综合人人澡精品| 久久九九久精品国产免费直播| 极品日韩久久| 欧美激情综合色| 一区二区高清视频| 国产精品日本精品| 欧美在线看片a免费观看| 黄色国产精品| 欧美成人dvd在线视频| 亚洲美女中出| 国产精品免费一区豆花| 欧美一区二区三区日韩视频| 经典三级久久| 欧美精品粉嫩高潮一区二区| 亚洲视频电影在线| 国产欧美日韩精品在线| 久久亚洲欧美| 一本久道久久综合狠狠爱| 国产精品色婷婷| 久久亚洲风情| 日韩午夜精品| 国产欧美日韩| 美女脱光内衣内裤视频久久影院 | 国产日韩视频| 欧美ab在线视频| 亚洲一区二区三区四区视频| 国产亚洲永久域名| 欧美高潮视频| 亚洲欧美激情四射在线日 | 在线观看三级视频欧美| 欧美日本一道本在线视频| 亚洲欧美综合| 亚洲福利视频一区| 国产精品超碰97尤物18| 久久久一区二区| 夜夜嗨av一区二区三区| 国产日韩欧美日韩大片| 欧美freesex交免费视频| 亚洲一区二区三区在线观看视频 | 91久久精品久久国产性色也91 | 在线观看国产成人av片| 欧美三级电影大全| 久久久一二三| 亚洲免费在线播放| 在线免费一区三区| 国产精品va在线播放| 久久影院午夜论| 亚洲一区免费网站| 亚洲国产精品久久精品怡红院| 国产精品国产三级国产专区53| 久久婷婷影院| 亚洲影院免费观看| 亚洲国产高清aⅴ视频| 国产精品系列在线| 欧美极品一区| 久久精品男女| 亚洲婷婷在线| 亚洲国产老妈| 国产亚洲女人久久久久毛片| 欧美女主播在线| 久久久亚洲影院你懂的| 亚洲小视频在线观看| 亚洲激情视频网站| 国产私拍一区| 国产精品草莓在线免费观看| 欧美国产一区二区| 久久视频一区二区| 欧美亚洲视频在线观看| 在线性视频日韩欧美| 亚洲国产裸拍裸体视频在线观看乱了中文| 国产精品夜夜夜| 欧美日韩亚洲精品内裤| 欧美a一区二区| 久久裸体视频| 欧美一区二区三区免费大片| 亚洲深夜激情| 亚洲毛片在线观看| 136国产福利精品导航| 国产亚洲一区在线播放| 国产精品免费视频观看| 欧美日韩免费观看一区三区| 欧美高清视频| 久久婷婷久久| 久久久成人精品| 欧美在线www| 亚洲欧美日韩一区在线观看| 国产精品99久久久久久久久久久久| 亚洲黄色一区二区三区| 在线观看成人av电影| 国产一区观看| 国产一区二区欧美| 国产视频不卡| 国产欧美日韩激情| 国产精品一区二区在线观看| 欧美视频手机在线| 欧美精品在线一区| 欧美激情一区二区三区全黄| 欧美777四色影视在线| 玖玖玖国产精品| 久久中文字幕导航| 麻豆成人在线观看| 美女黄毛**国产精品啪啪 | 国产精品国产三级国产专播品爱网| 欧美日本三级| 欧美久久久久中文字幕| 欧美精品v国产精品v日韩精品 | 欧美日韩国产综合在线| 欧美久久久久中文字幕| 欧美日韩亚洲在线| 欧美小视频在线| 国产精品美女一区二区| 国产精品久久亚洲7777|