Python內建資料結構之字典(完整版)
今天主要講解上次未完成的內建資料結構-字典。小白這幾天比較忙,忙的忘記了健身及寫作,特發此文以作補償。
Python字典簡介
Python內建了字典:dict的支援,dict全稱dictionary,在其他語言中也稱為map,使用鍵-值(key-value)儲存,具有極快的查詢速度。
這種key-value儲存方式,在放進去的時候,必須根據key算出value的存放位置,這樣,取的時候才能根據key直接拿到value。
請務必注意,dict內部存放的順序和key放入的順序是沒有關係的。
和list比較,dict有以下幾個特點:
- 查詢和插入的速度極快,不會隨著key的增加而增加;
- 需要佔用大量的記憶體,記憶體浪費多。
而list相反:
- 查詢和插入的時間隨著元素的增加而增加;
- 佔用空間小,浪費記憶體很少。
所以,dict是用空間來換取時間的一種方法。
字典的定義及初始化,
d = {} d = dict() d = {'a': 1, 'b': 2} d = dict([('a', 1), ('b', 2)]) # 可迭代物件的元素必須是一個二元組,二元組的第0個元素為字典的key,第1個元素為字典的value d = dict.fromkeys(range(5)) # 傳入的可迭代元素為key,值為None d = dict.fromkeys(range(5), 'abc') # 傳入的可迭代元素為key,值為abc
dict可以用在需要高速查詢的很多地方,在Python程式碼中幾乎無處不在,正確使用dict非常重要,需要牢記的第一條就是dict的key必須是不可變物件。這是因為dict根據key來計算value的儲存位置,如果每次計算相同的key得到的結果不同,那dict內部就完全混亂了。這個通過key計算位置的演算法稱為雜湊演算法(Hash)。
要保證hash的正確性,作為key的物件就不能變。在Python中,字串、整數等都是不可變的,因此,可以放心地作為key。而list是可變的,就不能作為key:
In[1]: d = {} In[2]: key = [1, 2, 3] In[3]: d[key] = 'a list' ---------------------------------------------------------------------- TypeError Traceback (most recent call last) TypeError: unhashable type: 'list'
字典常用方法
首先總結一下有哪些常用的方法:
- 增加:
update
- 刪除:
pop
,popitem
,clear
- 修改:
update
- 查詢:
get
- 其他:
keys
,values
,items
,fromkeys
字典增加或修改
update
方法會修改或增加字典內容。如有下一個字典:
In[12]: dict01 = {'laven': 23, 'taoqi': 20}
In[13]: dict01
Out[13]: {'laven': 23, 'taoqi': 20}
# 使用update增加一個key-value
In[14]: dict01.update(a=123)
In[15]: dict01
Out[15]: {'a': 123, 'laven': 23, 'taoqi': 20}
當然,我們直接使用dict01['a'] = 123
也是可以的,只不過我們這裡介紹的是字典的update
方法。接下來看看update
方法的修改作用:
In[15]: dict01
Out[15]: {'a': 123, 'laven': 23, 'taoqi': 20}
In[16]: dict01.update(laven=21)
In[17]: dict01
Out[17]: {'a': 123, 'laven': 21, 'taoqi': 20}
update
方法也可以接收一個二元組列表作為其引數來增加字典:
In[17]: dict01
Out[17]: {'a': 123, 'laven': 21, 'taoqi': 20}
In[18]: dict01.update([('b', 456), ('c', 789)])
In[19]: dict01
Out[19]: {'a': 123, 'b': 456, 'c': 789, 'laven': 21, 'taoqi': 20}
update
的引數也可以是一個字典,不過這種形式用的比較少,還是舉個例子吧:
In[20]: dict01
Out[20]: {'a': 123, 'b': 456, 'c': 789, 'laven': 21, 'taoqi': 20}
In[21]: dict01.update({'d': 987, 'e': 654})
In[22]: dict01
Out[22]: {'a': 123, 'b': 456, 'c': 789, 'd': 987, 'e': 654, 'laven': 21, 'taoqi': 20}
總結一下update的用法,它的引數的幾種情況:
- 可以是字典
- 可以是由二元組構成的可迭代物件
- 關鍵字引數
字典刪除
刪除字典有三種形式:
-
pop
刪除指定的key,返回該key的value -
popitem
隨機刪除,返回隨機刪除的一個kv二元組 -
clear
清空字典
接下來看例子:
In[23]: dict01 = {'laven': 23, 'taoqi': 20}
# pop一個存在的key
In[24]: dict01.pop('laven')
Out[24]: 23
# pop一個不存在的key呢?
In[25]: dict01.pop('lavenliu')
---------------------------------------------------------------------------
KeyError Traceback (most recent call last)<ipython-input-25-a346227feeaa> in <module>()----> 1 dict01.pop('lavenliu')
KeyError: 'lavenliu'
如果pop
一個不存在的key,為了不讓出現KeyError
異常,我們可以為其設定一個預設值,如下:
In[26]: dict01.pop('lavenliu', 'not exist')
Out[26]: 'not exist'
接下來看看popitem
方法:
In[27]: dict01
Out[27]: {'taoqi': 20}
In[28]: dict01['laven'] = 23
In[29]: dict01
Out[29]: {'laven': 23, 'taoqi': 20}
In[30]: dict01.popitem()
Out[30]: ('laven', 23) # 隨機返回一個二元組
In[31]: dict01
Out[31]: {'taoqi': 20}
如果是空字典呢?我們還能否進行popitem
方法呢?大家可以試試看。
最後看看字典的clear
方法:
In[34]: dict01 = {'laven': 23, 'taoqi': 20}
In[35]: dict01.clear()
In[36]: dict01
Out[36]: {}
如果空字典再次執行clear
方法會怎樣呢?大家可以試試看。
字典查詢
字典查詢也可以叫做字典的訪問,如果我們知道字典有哪些key,直接進行訪問就可以了。如下示例:
In[38]: dict01 = {'laven': 23, 'taoqi': 20}
In[39]: dict01['taoqi']
Out[39]: 20
In[40]: dict01['laven']
Out[40]: 23
我們使用get
方法試試呢?
In[41]: dict01.get('laven')
Out[41]: 23
# 訪問一個不存在的key呢
In[42]: dict01.get('lavenliu')# 發現什麼都沒有返回,我們可以給get方法一個預設值,
In[46]: dict01.get('lavenliu', -1)
Out[46]: -1
# 當對存在的key進行get時,返回的還是相應的value
In[47]: dict01.get('laven', -1)
Out[47]: 23
字典其他方法
字典的其他方法:
-
keys
返回所有的key -
values
返回所有的value -
items
返回一個二元組列表 -
fromkeys
可以批量建立字典的key-value
下面分別演示:
# keys方法演示
In[50]: dict01 = {'laven': 23, 'taoqi': 20}
In[51]: dict01.keys()
Out[51]: dict_keys(['laven', 'taoqi'])
# values方法演示
In[52]: dict01.values()
Out[52]: dict_values([23, 20])
# items方法演示
In[53]: dict01.items()
Out[53]: dict_items([('laven', 23), ('taoqi', 20)])
# fromkeys方法演示
In[54]: dict02 = {}
In[55]: dict02 = dict.fromkeys(range(5)) # 傳入的可迭代元素為keys
In[56]: dict02
Out[56]: {0: None, 1: None, 2: None, 3: None, 4: None}
In[57]: dict02 = dict.fromkeys(range(5), 'not none') # 還可以給一個預設值
In[58]: dict02
Out[58]: {0: 'not none', 1: 'not none', 2: 'not none', 3: 'not none', 4: 'not none'}
字典的遍歷
字典的遍歷很簡單:
In[48]: dict01
Out[48]: {'laven': 23, 'taoqi': 20}
In[49]: for k, v in dict01.items():
...: print(k, '=>', v)
...:
laven => 23
taoqi => 20
In[63]: for k in dict01.keys():
...: print(k, "=>", dict01[k])
...:
laven => 23
taoqi => 20
keys
、 values
、items
返回的都是生成器,它並不會複製一份記憶體。而Python2對應的方法返回的是列表,會複製一份。
還有一個不常用的方法叫做enumerate
,它返回的是key的所以及相應的key。示例如下:
In[64]: for idx, k in enumerate(dict01):
...: print(idx, "=>", k)
...:
0 => laven
1 => taoqi
# 還可以這樣使用
In[65]: for i, (k, v) in enumerate(dict01.items()):
...: print(i, k, "=>", v)
...:
0 laven => 23
1 taoqi => 20
根據value找其對應的key:
In[88]: d = {'a': 1, 'b': 2, 'c': 3}
In[89]: d.update(c=123)
In[90]: d
Out[90]: {'a': 1, 'b': 2, 'c': 123}
In[91]: for k, v in d.items():
...: if v == 123:
...: print(k)
...: break
...:
c
字典排序
由於字典是散列表,沒有順序,適合插入、查詢等操作。另外字典的key不一定是字串,但一定是不可變物件。
字典排序我們使用內建的sorted
函式:
In[107]: dict01
Out[107]: {'laven': 23, 'taoqi': 20}
In[108]: sorted(dict01.items(), key=lambda d: d[1], reverse=True)
Out[108]: [('laven', 23), ('taoqi', 20)]
In[109]: sorted(dict01.items(), key=lambda d: d[1], reverse=False)
Out[109]: [('taoqi', 20), ('laven', 23)]
預設字典
default初始化的時候,需要傳入一個函式,這個函式也叫工廠函式。當我們使用下標訪問一個key的時候,如果這個key不存在,defaultdict會自動呼叫初始化時傳入的函式,生成一個物件作為這個key的value。一個預設字典的例子,我們來構造一個多值的字典:
In[79]: from collections import defaultdict
# 常規方法
In[80]: d = {}
In[81]: for k in range(10):
...: for v in range(10):
...: if k not in d.keys():
...: d[k] = []
...: d[k].append(v)
...:
In[82]: d
Out[82]: {0: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
1: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
2: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
3: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
4: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
5: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
6: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
7: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
8: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
9: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]}
# 使用預設字典的方法
In[83]: d = defaultdict(list)
In[84]: d
Out[84]: defaultdict(list, {})
In[85]: print(d)defaultdict(<class 'list'>, {})
In[86]: for k in range(10):
...: for v in range(10):
...: d[k].append(v)
...:
In[87]: d
Out[87]: defaultdict(list,
{0: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
1: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
2: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
3: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
4: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
5: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
6: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
7: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
8: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
9: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]})
有序字典
在絕大多數的程式語言中,字典都是無序的。在Python中,字典也是無序的。但標準庫提供了有序字典的庫,我們可以建立有序字典。但是有序字典比常規的字典要佔用多一倍的記憶體空間。
示例如下:
In[4]: from collections import OrderedDict
In[5]: od = OrderedDict()
In[6]: od['a'] = 1
In[7]: od['b'] = 2
In[8]: od['c'] = 3
In[9]: od.keys()
Out[9]: odict_keys(['a', 'b', 'c'])
In[10]: for k, v in od.items()
:...: print(k, '->', v)
...:
a -> 1
b -> 2
c -> 3
字典的限制
- 字典的key不能重複;
- 字典的key需要可hash。
總結
今天,我們講解了字典的絕大部分的知識點。當然了Python字典的實現這裡就不講解了,可以作為拓展內容自己進行學習瞭解。這裡只是提一下,Python字典的實現主要有:“拉鍊法”和“開地址法”。