Python 字典操作進階

reer 8年前發布 | 6K 次閱讀 Python Python開發

學習了 Python 基本的字典操作后,學習這些進階操作,讓寫出的代碼更加優雅簡潔和 pythonic 。

與字典值有關的計算

問題

想對字典的值進行相關計算,例如找出字典里對應值最大(最小)的項。

解決方案一:

假設要從字典 {'a':3, 'b':2, 'c':6} 中找出值最小的項,可以這樣做:

>>> d = {'a':3, 'b':2, 'c':6}
>>> min(zip(d.values(), d.keys()))
(2, 'b')

值得注意的是 d.values() 獲取字典的全部值, d.keys() 獲取字典的全部鍵,而且兩個序列的順序依然保持一一對應的關系。因此 zip(d.values(), d.keys()) 實質上生成的是一個 (value, key) 的序列。 min 函數通過比較序列中的元組 (value, key) 找出其最小值。

解決方案二:

除了使用 zip(d.values(), d.keys()) 外,還可以使用 dict.items() 方法和生成器推導式來生成 (value, key) 序列,從而傳遞給 min 函數進行比較:

>>> d = {'a':3, 'b':2, 'c':6}
>>> min((v ,k) for (k, v) in d.items())
(2, 'b')

這里 min 函數的參數 (v ,k) for (k, v) in d.items() 其實是一個生成器推導式(和列表推導式一樣,只是把列表推導式的 [] 改為 () ,而且其返回的一個生成器而非列表),由于生成器推導式做為 min 函數的參數,所以可以省略掉兩邊的括號(不做為參數時寫法應該是 ((v ,k) for (k, v) in d.items()) )。

字典推導式

問題

想把一個元組列表轉換成一個字典,例如把 [('a', 1), ('b', 2), ('c', 3)] 轉化為 {'a': 1, 'b': 2, 'c': 3}

解決方案

類似于列表推導式,字典推導式可以方便地從其他數據結構構造字典,例如:

>>> l = [('a', 1), ('b', 2), ('c', 3)]
>>> {k: v for k, v in l}
{'c': 3, 'b': 2, 'a': 1}

字典推導式的規則和列表推導式一樣,只是把 [] 換成 {}

尋找字典的交集

問題

假設有兩個字典:

d1 = {'a':1, 'b':2, 'c':3, 'd':4}
d2 = {'b':2, 'c':3, 'd':3, 'e':5}

要找出這兩個字典中具有公共鍵的項,即要得到結果 {'b':2, 'c':3}

解決方案

我們知道一般通過 d.items() 方法來遍歷字典, d.items() 方法返回的對象是一個類集合對象,支持集合的基本運算,如取交集、并集等。

>>> dict(d1.items() & d2.items()) # 取交集
{'b': 2, 'c': 3}

此外, d.keys() 返回字典的鍵,也是一個類集合對象,如果我們只想找出兩個字典中鍵相同的項,可以這樣:

>>> { k:d1[k] for k in d1.keys() & d2.keys() }
{'b': 2, 'd': 4, 'c': 3}

這里如果相同的鍵對應不同的值則去第一個字典中的值。推廣開來,如果想排除掉字典中的某些鍵,可以這樣:

>>> { k:d1[k] for k in d1.keys() - {'c', 'd'} } # - 號的含義是集合的差集操作
{'b': 2, 'a': 1}

但有一點需要注意的是, d.values() 返回字典的值,由于字典對應的值不一定唯一,所以 d.values() 一般無法構成一個集合,因此也就不支持一般的集合操作。

多個字典連接成一個字典

問題

有多個字典,例如:

d1 = {'a':1, 'b':2, 'c':3}
d2 = {'c':4, 'd':5, 'e':6}

想將這多個字典連接為一個字典,或一次性對多個字典進行迭代操作。

解決方案

使用 collections.ChainMap :

>>> from collections import ChainMap

>>> chain_dict = ChainMap(d1, d2)
>>> for k, v in chain_dict.items():
        print(k, v)
a 1
e 6
d 5
c 3
b 2

ChainMap 將傳入的多個字典連接為一個字典,并返回一個 ChainMap 對象,這個對象的行為就像一個單一的字典,我們可以對其進行取值或者迭代等操作。注意到這里鍵 c 對應的值為 3,如果傳入 ChainMap 的字典含有相同的鍵,則對應的值為先傳入的字典中的值。

此外,如果你只想單純地迭代字典的鍵值對,可以結合使用 items() 和 itertools.chain() 方法:

>>> from itertools import chain
>>> for k, v in chain(d1.items(), d2.items()):
    print(k, v)

a 1
c 3
b 2
e 6
c 4
d 5

這里相同的鍵會被分別迭代出來。

保持字典有序

問題

想讓字典中元素的迭代順序和其加入字典的順序保持一致

解決方案

通常來說,使用 d.items() 或者 d.keys() 、 d.values() 方法迭代出來的元素順序是無法預料的。例如對字典 d = {'a':1, 'b':2, 'c':3} 迭代:

>>> d = dict()
>>> d['a'] = 1
>>> d['b'] = 2
>>> d['c'] = 3
>>> for k, v in d.items():
    print(k, v)

a 1
c 3
b 2

每一次運行結果都可能不同。如果想讓元素迭代的順序和創建字典時元素的順序一致,就要使用 collections.OrderedDict 代替普通的 dict :

>>> from collections import OrderedDict
>>> ordered_d = OrderedDict()
>>> ordered_d['a'] = 1
>>> ordered_d['b'] = 2
>>> ordered_d['c'] = 3
>>> for k, v in ordered_d.items():
    print(k, v)

a 1
b 2
c 3

OrderedDict 實際通過維護一個雙向鏈表來記錄元素添加的順序,因此其耗費的內存大約為普通字典的兩倍。所以在實際使用中需綜合考慮各種因素來決定是否使用 OrderedDict 。

使字典的鍵映射多個值

問題

通常情況下字典的鍵只對應一個值。現在想讓一個鍵對應多個值。

解決方案

為了使一個鍵對應多個值,首先需要把多個值放到一個容器中(例如列表或者集合等)。例如有這樣一個列表: [('a', 1), ('a', 2), ('b', 3), ('b', 4), ('c', 5)] ,我們要將其轉換成一個字典,保持元素的鍵值對應關系,通常我們會寫這樣的代碼:

>>> from pprint import pprint
>>> l = [('a', 1), ('a', 2), ('b', 3), ('b', 4), ('c', 5)]
>>> d = {}
>>> for k, v in l:
    if k in d:
        d[k].append(v)
    else:
        d[k] = [v]

>>> pprint(d)
{'a': [1, 2], 'b': [3, 4], 'c': [5]}

但是 if else 語句讓代碼顯得有點冗余和不易讀, Python 的 defaultdict 改善上述代碼。

>>> from collections import defaultdict
>>> d = defaultdict(list)
>>> for k, v in l:
    d[k].append(v)

>>> pprint(d)
defaultdict(<class 'list'>, {'c': [5], 'b': [3, 4], 'a': [1, 2]})

if else 的判語句沒有了。

defaultdict 是 dict 的一個子類。對 dict 來說,如果 key 不存在,則 dict[key] 取值操作會拋出 KeyError 異常,但是 defaultdict 則會返回一個傳入 defaultdict 構造器的類的實例(例如一個列表)或者自定義的缺失值。因此在上例中,對于 d[k].append(v) ,當 k 不存在時,則會先執行 d[k] = [] 并返回這個空列表,繼而將 v 加入到列表中。

傳入 defualtdict 構造器的值不一定要是一個類,也可以是一個可調用的函數,當相應的鍵不在 defualtdict 中時,其默認的值就為這個函數的返回值,例如:

>>> from collections import defaultdict
>>> def zero_default():
    return 0

>>> d = defaultdict(zero_default)
>>> d['a'] = 1
>>> d['a']
1

>>> d['b']
0

>>> d.keys()
dict_keys(['b', 'a'])
>>>

利用這樣一個特性,我們可以構造無限深度的字典結構:

>>> from collections import defaultdict
>>> import json
>>> tree = lambda: defaultdict(tree)
>>> d = tree()
>>> d['a']['b'] = 1
>>> print(json.dumps(d)) # 為了顯示的格式更好看
{"a": {"b": 1}}

這里當執行 d['a'] 時,由于相應的鍵不存在,故返回一個 defaultdict(tree) ,當再執行 d['a']['b'] = 1 時,將鍵 b 對應的值設為 1 。

 

來自:http://www.jianshu.com/p/65f4bebc1061

 

 本文由用戶 reer 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!