Python 字典操作進(jìn)階

學(xué)習(xí)了 Python 基本的字典操作后,學(xué)習(xí)這些進(jìn)階操作,讓寫出的代碼更加優(yōu)雅簡潔和 pythonic 。

與字典值有關(guān)的計(jì)算

問題

想對(duì)字典的值進(jìn)行相關(guān)計(jì)算,例如找出字典里對(duì)應(yīng)值最大(最小)的項(xiàng)。

解決方案一:

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

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

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

解決方案二:

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

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

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

字典推導(dǎo)式

問題

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

解決方案

類似于列表推導(dǎo)式,字典推導(dǎo)式可以方便地從其他數(shù)據(jù)結(jié)構(gòu)構(gòu)造字典,例如:

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

字典推導(dǎo)式的規(guī)則和列表推導(dǎo)式一樣,只是把 [] 換成 {}

尋找字典的交集

問題

假設(shè)有兩個(gè)字典:

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

要找出這兩個(gè)字典中具有公共鍵的項(xiàng),即要得到結(jié)果 {'b':2, 'c':3}

解決方案

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

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

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

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

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

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

但有一點(diǎn)需要注意的是,d.values() 返回字典的值,由于字典對(duì)應(yīng)的值不一定唯一,所以 d.values() 一般無法構(gòu)成一個(gè)集合,因此也就不支持一般的集合操作。

多個(gè)字典連接成一個(gè)字典

問題

有多個(gè)字典,例如:

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

想將這多個(gè)字典連接為一個(gè)字典,或一次性對(duì)多個(gè)字典進(jìn)行迭代操作。

解決方案

使用 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 將傳入的多個(gè)字典連接為一個(gè)字典,并返回一個(gè) ChainMap 對(duì)象,這個(gè)對(duì)象的行為就像一個(gè)單一的字典,我們可以對(duì)其進(jìn)行取值或者迭代等操作。注意到這里鍵 c 對(duì)應(yīng)的值為 3,如果傳入 ChainMap 的字典含有相同的鍵,則對(duì)應(yīng)的值為先傳入的字典中的值。

此外,如果你只想單純地迭代字典的鍵值對(duì),可以結(jié)合使用 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

這里相同的鍵會(huì)被分別迭代出來。

保持字典有序

問題

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

解決方案

通常來說,使用 d.items() 或者 d.keys()、d.values() 方法迭代出來的元素順序是無法預(yù)料的。例如對(duì)字典 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

每一次運(yùn)行結(jié)果都可能不同。如果想讓元素迭代的順序和創(chuàng)建字典時(shí)元素的順序一致,就要使用 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 實(shí)際通過維護(hù)一個(gè)雙向鏈表來記錄元素添加的順序,因此其耗費(fèi)的內(nèi)存大約為普通字典的兩倍。所以在實(shí)際使用中需綜合考慮各種因素來決定是否使用 OrderedDict 。

使字典的鍵映射多個(gè)值

問題

通常情況下字典的鍵只對(duì)應(yīng)一個(gè)值。現(xiàn)在想讓一個(gè)鍵對(duì)應(yīng)多個(gè)值。

解決方案

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

>>> 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 語句讓代碼顯得有點(diǎn)冗余和不易讀,Pythondefaultdict 改善上述代碼。

>>> 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 的判語句沒有了。

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

傳入 defualtdict 構(gòu)造器的值不一定要是一個(gè)類,也可以是一個(gè)可調(diào)用的函數(shù),當(dāng)相應(yīng)的鍵不在 defualtdict 中時(shí),其默認(rèn)的值就為這個(gè)函數(shù)的返回值,例如:

>>> 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'])
>>> 

利用這樣一個(gè)特性,我們可以構(gòu)造無限深度的字典結(jié)構(gòu):

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

這里當(dāng)執(zhí)行 d['a'] 時(shí),由于相應(yīng)的鍵不存在,故返回一個(gè) defaultdict(tree),當(dāng)再執(zhí)行 d['a']['b'] = 1 時(shí),將鍵 b 對(duì)應(yīng)的值設(shè)為 1 。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,825評(píng)論 6 546
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,814評(píng)論 3 429
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 178,980評(píng)論 0 384
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 64,064評(píng)論 1 319
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,779評(píng)論 6 414
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 56,109評(píng)論 1 330
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,099評(píng)論 3 450
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 43,287評(píng)論 0 291
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,799評(píng)論 1 338
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 41,515評(píng)論 3 361
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,750評(píng)論 1 375
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,221評(píng)論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,933評(píng)論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,327評(píng)論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,667評(píng)論 1 296
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 52,492評(píng)論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,703評(píng)論 2 380

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 134,836評(píng)論 18 139
  • 1. 字典的一些知識(shí)點(diǎn) 字典特性可變、可存儲(chǔ)任意類型對(duì)象、無序 字典的生成?直接用dict 字典的排序?sorte...
    海螺上的斑點(diǎn)閱讀 411評(píng)論 0 0
  • 本文為《爬著學(xué)Python》系列第九篇文章。 從現(xiàn)在開始算是要進(jìn)入“真刀真槍”的Python學(xué)習(xí)了。之所以這么說,...
    SyPy閱讀 2,158評(píng)論 0 14
  • 本節(jié)要介紹的是Python里面常用的幾種數(shù)據(jù)結(jié)構(gòu)。通常情況下,聲明一個(gè)變量只保存一個(gè)值是遠(yuǎn)遠(yuǎn)不夠的,我們需要將一組...
    小黑y99閱讀 65,216評(píng)論 0 9
  • 楊瀾在談子女教育時(shí)說過,不要做情緒化的媽媽。 可是現(xiàn)實(shí)生活中,在遇到孩子調(diào)皮搗亂不聽話時(shí),爸媽們還是會(huì)忍不住心里的...
    陳艷_2659閱讀 726評(píng)論 0 0