資料型別第2篇「字典和集合的原理和應用」

清菡發表於2020-12-16

堅持原創輸出,點選藍字關注我吧

作者:清菡
部落格:oschina、雲+社群、知乎等各大平臺都有。

由於微信公眾號推送改為了資訊流的形式,防止走丟,請給加個星標 ⭐,你就可以第一時間接收到本公眾號的推送!

目錄

  • 一、集合
    • 1.定義個有元素的集合
    • 2.自動去重
    • 3.集合常用的五個方法
  • 二、集合和字典都是無序的
  • 三 、字典和集合都是無序的,在記憶體中是怎麼儲存?
    • 1.為什麼說字典和集合是無序的?
    • 2.字典查詢值的過程
    • 3.Python 裡基礎資料型別分為三大類
    • 4.為什麼會出現雜湊衝突?
  • 四、可變和不可變元素:可雜湊和不可雜湊
    • 1.可變型別的資料不可進行雜湊運算,不可變的資料型別可進行雜湊運算
    • 2.集合為什麼無序?
    • 3.雜湊型別為什麼是無序的?
  • 五、效能分析

本篇文章:重點掌握集合的用法即可。

字典,大家都用得特別多,花括號包起來的,一個鍵一個值構成一個元素。集合和字典的表達形式是一樣的。

字典和集合在 Python 中都是使用花括號進行表示的。

一、集合

1.定義個有元素的集合

set1 = {1,2,3}

集合和字典相比,集合裡面只有值,沒有鍵。

2.自動去重

集合有個比較強大的功能:自動去重。 裡面不會存在重複的元素,集合最常見的應用就是對列表去重。

2.1 把字典轉換成集合,再轉換回字典,它會真去重

set1 = {1,2,3,3,3,4,4,4,4,4}
print(set1)

列印出來是集合,重複的元素自動過濾掉了。定義的時候,不管定義多少個重複元素,都自動過濾掉了。

2.2 用集合對列表去重

li = [1,1,1,2,2,2,3,3,3] # 利用集合對列表去重
li2 = list(set(li))
print(li2)

首先把列表轉換成一個集合,自動把裡面的重複元素給去除掉了,再轉換回列表。

集合在 Python 中是用得比較少的資料型別。

3.集合常用的五個方法

add() 新增元素
update() 更新元素
remove() 刪除元素
clear() 清空裡面所有的元素
copy() 複製元素

集合,它裡面的元素是無序的。可以修改,集合是可變型別的資料。

3.1 空集合中怎麼新增元素?

add()方法,每次可以往裡面新增一個資料進去。

se = set()  # 空集合
# 集合新增資料
se.add('qinghan') # 一次只能新增一個,所以只新增了一個qinghan
print(se)

3.2 刪除用 remove()

集合可以新增也可以刪除。刪除用remove(),傳入對應的元素就可以進行刪除。

集合還可以做交集、並集這樣的操作,這個對我們用處不大。

3.3update() 更新元素

跟字典的update()一樣的。 它是將一個集合更新到這個集合裡面,可以往裡面一次加入多個元素。

通過這個方法:

se = set()  # 空集合
se.update({111,22,33,44})
print(se)

可以一次更新進去多個元素。

update的原始碼:

接收的是不定量引數,可以傳一個也可以傳多個。

可以往裡面加元組、列表、字串,但是一般用的時候選擇用集合,將一個集合更新到原來的集合裡面。

3.4clear()清空元素

還有個常用的方法:clear()清空裡面所有的元素。

3.5 copy()複製元素

copy() # 做一個複製的

二、集合和字典都是無序的

Python 裡面把它稱作雜湊型別。

Python 更新到 3.7 之後,字典出現一個新的特性:3.7 之前的字典是無序的。3.7 之後字典中元素的順序,它會按你依次新增的順序進行儲存。現在字典,裡面的元素實際上是有序的。

官方文件已宣告:

三 、字典和集合都是無序的,在記憶體中是怎麼儲存?

dict 與 set 實現原理是一樣的,都是將實際的值放到 list 中。

唯一不同的在於 hash 函式操作的物件,對於 dict,hash 函式操作的是其 key,而對於 set 是直接操作的它的元素。

假設操作內容為 x,其作為因變數,放入 hash 函式,通過運算後取 list 的餘數,轉化為一個 list 的下標,此下標位置對於 set 而言用來放其本身。

而對於 dict 則是建立了兩個 list,一個 list 該下表放此 key,另一個 list 中該下標對應的 value。

其中,我們把實現 set 的方式叫做 Hash Set,實現 dict 的方式叫做 Hash Map/Table(注:map 指的是通過 Key 來尋找 value 的過程)。

1.為什麼說字典和集合是無序的?

1.1 字典和集合底層都是儲存在列表裡面

一個字典,在儲存的時候,會拆分成 2 部分,會存在 2 個列表裡面,一個列表存鍵,一個列表存值:

字典儲存時的拆分

1.2 怎麼通過 Key 找到對應的 Value 值呢?

字典在儲存之前,做了個 Hash 操作:

Hash操作如圖,圖片來自網路

拿到字典的鍵,進行雜湊操作。通過對應的雜湊演算法,然後得出一串數字。

拿雜湊出來的值除以記憶體分出來的列表的長度,得到餘數。這個餘數當成對應元素的下標。把鍵和值通過下標存在列表中對應的位置。

1.3 雜湊型別的儲存過程

雜湊型別的儲存過程,圖片來自網路

雜湊型別的意思就是無序的。 雜湊就是雜湊。雜湊內部元素是無序的。

剛開始記憶體分了 12 個格子存資料,雜湊後,第一個元素得出的餘數是 6,有 2 個列表,會把鍵存在對應的列表裡面,把值存在對應的 6 的位置。

雜湊表儲存資料很鬆散,不像列表完整得排過來的。雜湊表裡面是分散儲存的,會把對應的鍵存到一個雜湊表裡面。

查詢字典中元素的時候,首先它會拿到你這個鍵,同樣進行雜湊運算。運算完畢後得出一個值,然後去雜湊表裡面找對應的鍵。找到對應的鍵,然後比較下是不是這個鍵。

字典雜湊的是它的鍵,不是它的值。集合是雜湊的它的值,所以集合裡面的值是不可變型別的,不能有可變型別的值。

2.字典查詢值的過程

字典查詢值的過程

雜湊值就是雜湊值。拿到鍵名,進行雜湊,雜湊過後得到雜湊值。

拿到雜湊值進行相應的運算,然後拿到表元。表元是在雜湊表中的一個序號。

2.1 第一種情況

比如序號是 6,看 6 裡面存的這個鍵,跟你剛才輸進來查詢的那個鍵是不是一樣的。

如果是一樣的,鍵相等,會返回表元裡面對應的值,會給你找到你所儲存的字典的值。

如果它在這裡沒找到值的話,這個時候會丟擲異常。(也就是字典通過鍵去找值,沒找到的時候就會丟擲錯誤。)

2.2 第二種情況

雜湊衝突:

每個元素雜湊出來的結果是不一樣的。如圖,第一個元素計算出來是 6,會找到雜湊表中第 6 個格子。第二個值,運算之後,如果得出來的也是個 6,那麼這個時候就會起雜湊衝突。

解決雜湊衝突有二種方案:

方案一:

有雜湊衝突的時候,會對雜湊表進行擴容,擴容後進行重新排序。

方案二:

在後面再加個列表。這樣的話,第一個元素計算出來是 6,會找到雜湊表中第 6 個格子。

第二個值,運算之後,如果得出來的也是個 6,因為加了一個列表(這個列表可儲存多個值),就不會起雜湊衝突了。

以上是字典,雜湊型別底層儲存。

3.Python 裡基礎資料型別分為三大類

第一類,數值型別: 1 一個數只有單個元素,像這個 1 就是 1。

第二類,序列型別: 字串、列表、元組。

第三類,雜湊型別: 字典、集合。 特徵:內部元素是無序的。

4.為什麼會出現雜湊衝突?

舉個例子:

這兩個資料通過雜湊,計算雜湊值,取餘後拿到的餘數,如果是一樣的話,在儲存值的時候,就會造成雜湊衝突。

通過字典的鍵去雜湊,把雜湊值存在雜湊表裡面。通過對應的鍵,然後找到列表中儲存的對應元素的值。

集合相對於列表比較簡單一些。集合沒有鍵和值,直接拿到集合裡面的值進行雜湊操作。

四、可變和不可變元素:可雜湊和不可雜湊

1.可變型別的資料不可進行雜湊運算,不可變的資料型別可進行雜湊運算。

集合裡面只能儲存可雜湊的物件。意思是集合裡面只能儲存不可變的資料型別。

例如:set2 = {1,2,3,[1,2]}

這個集合就報錯了:

因為列表是可變型別。可變型別是不能進行雜湊運算的。

數值型別、字串、元組可以,列表、字典、集合不能作為元素儲存在這個集合裡面。

集合裡面的元素通過雜湊操作算出對應值,放到雜湊表裡面。

2.集合為什麼無序?

因為雜湊表裡面儲存元素的時候是沒有順序的,雜湊表也是會不斷變化的(會變化長度、調整元素位置的),所以說雜湊型別是無序的。

3.雜湊型別為什麼是無序的?

通過雜湊演算法算了之後,然後存到對應的雜湊表裡面,雜湊表裡面資料儲存是沒有固定順序的。

五、效能分析

字典最佔用記憶體,其次是集合。然後是列表、元組。元組是佔用記憶體最少的。但是查詢元素的時候,集合是速度最快的,然後是字典。

集合用起來不方便,如果知道哪個元素就好查詢,但是不知道那個元素在哪裡,就不方便從集合裡去取那個元素。字典通過鍵取值,元組、列表通過下標。


公眾號 「清菡軟體測試」 首發,更多原創文章:清菡軟體測試 107+原創文章,歡迎關注、交流,禁止第三方擅自轉載。

相關文章