字典與集合高級用法

dict 類型不但在各種程序里廣泛使用,它也是 Python 語言的基石。模塊的命名空間實(shí)例的屬性和函數(shù)的關(guān)鍵字參數(shù)中都可以看到字典的身影晰洒。

第三章 字典和集合

可散列的數(shù)據(jù)類型:

如果一個對象是可散列的,那么在這個對象的生命周期中啥箭,它的散列值是不變的谍珊,而且這個對象還需要實(shí)現(xiàn)__hash__()方法,另外可散列對象還需要有__eq__()方法急侥,這樣才能跟其他鍵做比較砌滞。

字典提供了很多種構(gòu)造方法:

a = dict(one=1, two=2)
b = {'one':1, 'two':2}
c = dict(zip(['one', 'two'], [1, 2]))
d = dict([('one', 1), ('two', 2)])
e = dict({'one': 1, 'two': 2})

>>> a == b == c == d == e
True 

除了這些字面語法和靈活的構(gòu)造元素以外,字典推導(dǎo)也可以用來建造新的 dict:

a = ['s', 'o', 's']
b = {k: v for k, v in enumerate(a)}

>>> print(b)
{0: 's', 1: 'o', 2: 's'}

使用 setdefault 來處理找不到的鍵

當(dāng)字典 d[k] 不能找到正確的鍵時坏怪, Python 會拋出異常贝润,這個行為符合 Python 所信奉的”快速失敗“哲學(xué),我們都知道可以用 d.get(k, default)來代替 d[k]铝宵,給找不到的鍵一個默認(rèn)的返回值打掘。但是要更新某個鍵對應(yīng)的值時,不管使用 __getitem__ 還是 get 都會不太自然鹏秋,而且效率低尊蚁。就像如下還沒有經(jīng)過優(yōu)化的代碼所顯示的那樣,dict.get 并不是處理找不到鍵的最好方式侣夷。

# -*- coding: utf-8 -*-
"""
@Desc    : 獲取一段文本中的單詞出現(xiàn)的頻率以及出現(xiàn)的位置横朋,如(1, 2), 橫縱坐標(biāo)都以 1 開始
"""

import sys
import re


# 創(chuàng)建正則 Pattern 對象,確保進(jìn)行正則操作時不用重復(fù)創(chuàng)建 Pattern 對象
WORD_RE = re.compile(r'\w+')

index = {}

# sys.argv 是一個列表惜纸,sys.argv[0] 一般是"被調(diào)用的腳本名或全路徑"叶撒,sys.argv[1] 以及以后的都是傳入的參數(shù)
# 如 python main.py ../../data/zen.txt
# sys.argv[0] 就是 main.py
# sys.argv[1] 就是 ../../data/zen.txt
with open(sys.argv[1], encoding='utf-8') as fp:
    # enumerate 有兩個參數(shù)绝骚,第一個參數(shù)是一個 Iterable(可迭代對象),第二個參數(shù) start 是開始位置祠够,默認(rèn)為 0
    for line_no, line in enumerate(fp, 1):
        # re.finditer(pattern, str) <=> pattern.finditer(str)压汪,返回一個iterator(迭代器)
        for match in WORD_RE.finditer(line):
            word = match.group()
            column_no = match.start() + 1
            location = (line_no, column_no)              
            # 提取 word 出現(xiàn)的情況,如果還沒有該記錄止剖,返回[], 這不是一種好的寫法
            occurrences = index.get(word, [])            # ①
            # 將新單詞出現(xiàn)的位置添加到列表的后面
            occurrences.append(location)                 # ②
            # 將新列表放回到字典中穿香,這里又牽扯到一次查詢操作
            index[word] = occurrences                    # ③

# sorted 函數(shù)的 key=參數(shù)沒有調(diào)用str.upper绎速,而是把這個方法的引用傳遞給 sorted 函數(shù)
# 這樣在排序的時候皮获,就會按照str.upper處理后的方式進(jìn)行排序纹冤。
for word in sorted(index, key=str.upper):
    print(word, len(index[word]))

上述處理單詞的三行(① ② ③),通過 dict.setdefalut 可以只有一行就解決雁歌。

...
location = (line_no, column_no)
# 獲取單詞的出現(xiàn)情況列表,如果單詞不存在靠瞎,把單詞和一個空列表放進(jìn)映射求妹,
# 然后返回這個空列表,這樣就能在不進(jìn)行第二次查找的情況下更新列表了
index.setdefault(word, []).append(location)
...

即:

my_dict.setdefault(key, []).append(new_value)

<=>

if key not in my_dict:
    my_dict[key] = []
my_dict[key].append(new_value)

只不過后者至少要進(jìn)行兩次鍵查詢丑勤,如果鍵不存在的話吧趣,就是三次,用 setdefault 只需要一次就可以完成整個操作强挫,以上是針對通過查找來插入新值時的最佳解決方式

字典的變種

  • collections.OrderedDict, 有序字典呆细,在添加鍵的時候會保持順序,因此鍵的迭代次序總是一致的八匠。OrderedDict 的 popitem 默認(rèn)刪除并返回的是字典里的最后一個元素趴酣,但是如果 popitem(last=False) 則刪除并返回第一個被添加進(jìn)的元素岖寞。
  • collections.ChainMap柜蜈,該類型可以容納數(shù)個不同的映射對象,在進(jìn)行鍵查找的時候淑履,這些對象會被當(dāng)做一個整體被逐步查找,直到鍵被站到位置狸吞。
  • collections.Counter,該類型會給鍵準(zhǔn)備一個整數(shù)計(jì)算器捷绒,每次更新一個鍵的時候都會增加這個計(jì)數(shù)器。所以這個類型還可以用來給可散列對象計(jì)數(shù)贯要。Counter 實(shí)現(xiàn)了 + 和 - 運(yùn)算符用來合并記錄椭住,還有 most_common([n]),它會按照次序返回映射里最常見的 n 個鍵和他們的計(jì)數(shù)京郑。
  • collections.UserDict,該類就是把標(biāo)準(zhǔn)的 dict 用純 Python 又實(shí)現(xiàn)了一遍跟狱,跟上述這些開箱即用的類型不同户魏, UserDict 是讓用戶繼承寫子類(自定義映射類型)的。

創(chuàng)建自定義映射類型

就創(chuàng)造自定義映射類型來說关翎,以 UserDict 為基類,總比以普通的 dict 為基類要來得方便纵寝。dict 會在某些方法的實(shí)現(xiàn)上走一些捷徑星立,導(dǎo)致我們不得不在它的子類中重寫這些方法葬凳。

UserDict 并不是 dict 的子類室奏,但是 UserDict 有一個叫作 data 的屬性,是 dict 的實(shí)例荐健,這個屬性實(shí)際上是 UserDict 最終存儲數(shù)據(jù)的地方琳袄。

'''
自定義字符串鍵字典
無論是添加,更新還是查詢操作址否,StrKey都會把非字符串的鍵轉(zhuǎn)換為字符串
'''
import collections


class StrKeyDict(collections.UserDict):
    def __missing__(self, key):
        if isinstance(key, str):
            raise KeyError(key)
        return self[str(key)]
    
    def __contains__(self, key):
        # 可以放心假設(shè)所有已經(jīng)存儲的鍵都是字符串碎紊,所以只要在self.data中查詢就好了
        return str(key) in self.data
    
    def __setitem__(self, key, item):
        # self.data 為 dict 的實(shí)例
        self.data[str(key)] = item

字典中的散列表

散列表:其實(shí)是一個稀疏數(shù)組(總有空白元素的數(shù)組稱為稀疏數(shù)組)。一般將散列表里的單元稱為表元音同,在 dict 的散列表中,每個鍵值對都占用一個表元权均,每個表元有兩個部分锅锨,分別是是對鍵、對值的引用必搞,因?yàn)樗斜碓拇笮∫恢拢钥梢酝ㄟ^偏移量來讀取某個表元塔橡。

因?yàn)?Python 會設(shè)法保證大概還有三分之一的表元是空的,所以在快要達(dá)到這個閥值的時候谱邪,原有的散列表會復(fù)制到一個更大的空間里庶诡。

字典的限制

  1. 鍵必須是可散列的
  2. 字典在內(nèi)存上的開銷巨大,由于散列表必須是稀疏的扯俱,這導(dǎo)致字典在空間上的效率低下,所以迅栅,如果你要存放數(shù)量巨大的記錄,那么放在由元組或是具名元組構(gòu)成的列表中會是比較好的選擇为流。
  3. 往字典里添加新鍵可能會改變已有鍵的順序让簿。無論何時往字典里添加新的鍵,Python 解釋器都可能做出為字典擴(kuò)容的決定尔当。擴(kuò)容導(dǎo)致的結(jié)果就是要新建一個更大的散列表,并把已有的元素添加到新表里锐帜,這個過程可能會發(fā)生新的散列沖突畜号,導(dǎo)致新建列表中鍵的次序變化。如果你在迭代一個字典的所有鍵的過程中同時對字典進(jìn)行修改简软,那么這個循環(huán)很有可能會跳過一些鍵——甚至是跳過那些字典中已經(jīng)有的鍵。

集合

集合字面量,{1}, {1, 2}贸典,但是如果是空集,必須寫成set()的形式据过。

集合推導(dǎo),{ i for i in range(10)}绳锅。

集合可用于去重酝掩。

集合的特點(diǎn):

  • 集合里的元素必須是可散列的
  • 集合很消耗內(nèi)存
  • 可以很高效的判斷元素是否存在與某個集合
  • 元素的次序取決于被添加到集合里的次序
  • 往集合里添加元素,可能會改變集合里已有元素的次序
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末原朝,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子鞠评,更是在濱河造成了極大的恐慌壕鹉,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,561評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件晾浴,死亡現(xiàn)場離奇詭異,居然都是意外死亡敬鬓,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,218評論 3 385
  • 文/潘曉璐 我一進(jìn)店門钉答,熙熙樓的掌柜王于貴愁眉苦臉地迎上來数尿,“玉大人,你說我怎么就攤上這事右蹦。” “怎么了何陆?”我有些...
    開封第一講書人閱讀 157,162評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長贷盲。 經(jīng)常有香客問我剥扣,道長,這世上最難降的妖魔是什么钠怯? 我笑而不...
    開封第一講書人閱讀 56,470評論 1 283
  • 正文 為了忘掉前任晦炊,我火速辦了婚禮宁脊,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘朦佩。我一直安慰自己庐氮,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,550評論 6 385
  • 文/花漫 我一把揭開白布弄砍。 她就那樣靜靜地躺著,像睡著了一般慨畸。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上寸士,一...
    開封第一講書人閱讀 49,806評論 1 290
  • 那天弱卡,我揣著相機(jī)與錄音,去河邊找鬼婶博。 笑死,一個胖子當(dāng)著我的面吹牛凡人,可吹牛的內(nèi)容都是我干的叹阔。 我是一名探鬼主播,決...
    沈念sama閱讀 38,951評論 3 407
  • 文/蒼蘭香墨 我猛地睜開眼忠荞,長吁一口氣:“原來是場噩夢啊……” “哼帅掘!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起修档,我...
    開封第一講書人閱讀 37,712評論 0 266
  • 序言:老撾萬榮一對情侶失蹤吱窝,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后院峡,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,166評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡发魄,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,510評論 2 327
  • 正文 我和宋清朗相戀三年俩垃,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片苹粟。...
    茶點(diǎn)故事閱讀 38,643評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡跃闹,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出望艺,到底是詐尸還是另有隱情,我是刑警寧澤荣茫,帶...
    沈念sama閱讀 34,306評論 4 330
  • 正文 年R本政府宣布啡莉,位于F島的核電站,受9級特大地震影響咧欣,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜衩椒,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,930評論 3 313
  • 文/蒙蒙 一哮兰、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧喝滞,春花似錦、人聲如沸右遭。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,745評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至图筹,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間劝篷,已是汗流浹背民宿。 一陣腳步聲響...
    開封第一講書人閱讀 31,983評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留活鹰,地道東北人哈恰。 一個月前我還...
    沈念sama閱讀 46,351評論 2 360
  • 正文 我出身青樓着绷,卻偏偏與公主長得像锌云,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子桑涎,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,509評論 2 348

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