輕鬆初探 Python 篇(五)— dict 和 set 知識彙總

WeaponZhi發表於2017-11-30

輕鬆初探 Python 篇(五)— dict 和 set 知識彙總

這是「AI 學習之路」的第 5 篇,「Python 學習」的第 5 篇

小之的公眾號 : WeaponZhi

介紹一下 dict 和 set 這兩個資料結構。

dict

dict 是 Python 內建的字典型別,熟悉 Java 的同學可以把它類比為 Map。dict 使用鍵值對來儲存(key-value),它的查詢速度特別快。

dict 一般用在什麼場景呢?假設我們需要根據公司名字查詢公司地址,按照我們之前的寫法,我們需要先建立兩個 list ,一個儲存公司名字,一個儲存公司總部地址,然後查詢公司名字,記錄好列表位置,再從地址列表查詢到具體元素,你還得保證兩個表元素位置必須一一對應。不僅如此,如果表很長,那遍歷查詢效率將會非常低。

>>> Inc = ['騰訊','阿里','百度']
>>> adress = ['深圳','杭州','北京']
>>> BaiduAdress = adress[Inc.index('百度')]
>>> BaiduAdress
'北京'
複製程式碼

我們現在用 dict 實現,使用一個「公司-地址」這樣的鍵值對來進行儲存資料,查詢的時候,我們只需要輸入公司名字,就可以查詢到對應的地址,同時,不論 dict 的資料有多少,查詢單項的速度都是一樣的,而且非常迅速。

>>> Inc_dict = {'騰訊':'深圳','阿里':'杭州','百度':'北京'}
>>> Inc_dict['百度']
'北京'
複製程式碼

dict 速度這麼快的原理就是使用了空間換取時間的方法,將無限集對映到一個有限集中。通過一個雜湊函式來計算每一個 key 應該存放在記憶體中的位置,然後把 value 儲存在記憶體的這個位置上,等到需要取出 key 對應的 value 的時候,只需要通過函式計算出這個位置,然後直接去拿就行了。是不是有點像我們查字典的步驟呢?

通過雜湊函式求出的最終值就是對應的雜湊值(Hash),Java 中的 Map 最常用的實現 HashMap 也是用類似的原理來設計的。Hash 演算法也是資料結構中特別重要的一個知識點,所以如果我們計算機的基本功紮實,學哪門語言的時候都是融會貫通的

當然,雜湊函式本身比較複雜,還要牽扯到衝突的解決問題,簡單來說,不同的 key 通過雜湊函式求得的記憶體位置可能是一樣的,這樣就導致了衝突,解決這種衝突的方法有很多,Python 設計者選擇了開放定址法,在衝突的時候用另一個不同的函式再計算。在這裡我就不深入討論了,有興趣的同學可以查閱下相關資料。

我們有很多種方式進行 dict 的初始化,下面幾種初始化方式都會獲得{"one": 1, "two": 2, "three": 3}:

>>> a = dict(one=1,two=2,three=3)
>>> b = {'one':1,'two':2,'three':3}
>>> c = dict(zip(['one','two','three'],[1,2,3]))
>>> d = dict([('two',2),('one',1),('three',3)])
>>> e = dict({'three':3,'one':1,'two':2})
>>> a == b == c == d == e
True
複製程式碼

除了通過初始化以外,還可以通過 key 來放入值,再次傳入相同 key ,不同 value,將會覆蓋前面傳入的 value。如果某個 key 不存在,獲取該 key 的 value 將會報 KeyError 錯誤。

>>> Inc_dict['途牛'] = '南京'
>>> Inc_dict['途牛']
'南京'
>>> Inc_dict['途牛'] = '金陵'
>>> Inc_dict['途牛']
'金陵'
>>> Inc_dict['小米']
KeyError:'小米'
複製程式碼

為了防止獲取 key 不存在的情況。我們可以用 in 來判斷 dict 中是否已經儲存過以這個 key 來儲存的鍵值對。或者用 get() 方法來獲取 value,如果 key 不存在,get() 將返回 None,可以設定一個引數來表示 key 不存在時候的預設返回值。

>>> '小米' in Inc_dict
False
>>> Inc_dict.get('小米')
>>> Inc_dict.get('小米','北京')
北京
複製程式碼

通過 pop(key) 方法,來返回並刪除對應的 value:

>>> Inc_dict.pop('騰訊')
'深圳'
>>> Inc_dict
{'阿里':'杭州','百度':'北京','途牛':'金陵'}
複製程式碼

最後介紹下 dict 的迭代,我們知道 list 迭代可以簡單的通過 for 來遍歷,dict 迭代需要多做一些操作。

>>> d = {'a':1,'b':2,'c':3}
>>> for key in d:
...     print(key)
...
'a'
'c'
'b'
複製程式碼

dict 預設的迭代方式是迭代 key ,如果你需要迭代 value 可以通過 d.values() 來獲取 value 的列表

>>> for value in d.values()
...     print(value)
...
1
3
2
複製程式碼

當然,你還可以同時迭代 key 和 value

>>> for k, v in d.items():
...     print(k, v)
...
a 1
c 3
b 2
複製程式碼

細心的同學一定發現了迭代的順序和我們初始化定義的順序是不同的,之前也提到了,dict 內部存放順序是根據雜湊函式決定的,所以最後的存放順序不一定和插入順序一致,那我們迭代順序顯然是不確定的了。

dict 的設計是典型的以空間換取時間,大家學習 Python 越深入就會發現 Python 的設計裡有很多這樣的設計, Python 設計的時候,大概已經不是記憶體最大就 4,500K 的年代了吧(手動嬉笑)。所以 dict 的特點就是,查詢和插入的速度非常快,並且不隨元素數量的增長而變慢。

注意:key 必須是不可變物件(字串,整數等),如果 key 是 list,就會報錯 TypeError: unhashable type: 'list',tuple 雖然是不可變物件,但如果傳入的 tuple 元素有可變物件,依然會報錯。

>>> d = {'a':1}
>>> d = {'a':1,(1,):2}
>>> d = {'a':1,(1,):2,(1,[1]):3}
TypeError: unhashable type: 'list'
複製程式碼

set

set 和 dict 很像,不過 set 不儲存鍵值對,你可以把它想像成只儲存 key 的 dict,也可以理解成數學中的無序無重複集合這個概念。所以在 set 中是沒有重複元素的,也只能存放不可變元素。我們可以通過一個 list 來建立 set。同樣,也是用大括號表示。

>>> s = set([1,2,3])
>>> s
{1,2,3}
>>> s = set([1,2,3,3,3])
>>> s
{1,2,3}
複製程式碼

我們可以看到,重複的元素自動被過濾了,同時 set 也是無序的,雖然建立時候顯示看起來好像是有序的。我們來看看 set 的一些常用方法。

>>> s.add(4)
>>> s
{1,2,3,4}
>>> s.add(4)
>>> s
{1,2,3,4}
複製程式碼

add(key)新增元素到 set 中,但新增重複元素將不會生效

>>> s.remove(4)
>>> s
{1,2,3}
>>> s.remove(4)
KeyError: 4
>>> s1 = {1,2,3}
>>> s2 = {2,3,4}
>>> s1 & s2
{2,3}
>>> s1 | s2
{1,2,3,4}
複製程式碼

remove(key)刪除元素,如果 key 不存在會報錯。同時,set 之前說過可以看成是集合,所以可以做一些交併集的操作。


歡迎關注我的公眾號

輕鬆初探 Python 篇(五)— dict 和 set 知識彙總

相關文章