《Fluent Python》讀書筆記-Dictionaries and Sets

概覽

????在collections.abc中定義了MappingMutableMapping去定義dict和類似類型的接口点额。一般的特殊類型的映射(mappings)都是繼承自dict或者collections.UserDict著蛙。

Hashable

????所有在標(biāo)準(zhǔn)庫里的映射類型都是繼承自dict,所以他們都有一個限制,key必須是hashable的均牢。
????一個對象是hashable的有三點:
1.它有一個hash值在整個生命周期內(nèi)都是不變的(需要一個__hash__()方法)。
2.能夠和其他對象比較(需要一個__eq__()方法)
3.hashable的對象如果比較是相等的必須有相同的hash值。
????常見的hashable的類型:
1.原子的不可變類型(immutable)都是hashable的亿汞,比如str, bytes, numeric類型。
2.frozenset總是hashable的揪阿,因為它的元素要求必須都是hashable的疗我。
3.tuple如果所有的元素都是hashable的就是hashable的咆畏。
4.用戶自定義類型缺省是hashable的,因為他們的hash值就是他們的id()并且是不相等的吴裤。如果一個對象實現(xiàn)了自定義__eq__方法旧找,就需要考慮到這個對象的內(nèi)部狀態(tài),只有當(dāng)所有的屬性是immutable的這個對象才是hashable的麦牺。

>>> tt = (1, 2, (30, 40))
>>> hash(tt)
8027212646858338501
>>> tl = (1, 2, [30, 40])
>>> hash(tl)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'
>>> class A:
...     def __init__(self):
...         pass
... 
>>> a = A()
>>> b = A()
>>> hash(a)
-9223372036571095159
>>> hash(b)
283680646
>>> a == b
False

????關(guān)于__hash__()__eq__的自定義這里還有些特殊的要求钮蛛,詳細(xì)可以看python的手冊中相關(guān)的內(nèi)容
????這里還引申出另一個問題就是is==的區(qū)別剖膳。is比較的是對象的id是否相同魏颓,意義在于是否是同一個實例對象,是否是同一個內(nèi)存地址潮秘。而==比較的是兩個對象的內(nèi)容是否相同琼开,默認(rèn)會調(diào)用對象的__eq__方法。繼承自 object 的__eq__方法比較兩個對象的id枕荞,結(jié)果與 is 一樣柜候。但是多數(shù)Python的對象會覆蓋object的__eq__方法,而定義內(nèi)容的相關(guān)比較躏精,所以比較的是對象屬性的值渣刷。

>>> a = [1, 2, 3]
>>> b = a
>>> b is a
True
>>> b == a
True
>>> b = a[:]
>>> b is a
False
>>> b == a
True

????數(shù)字類型用is比較有一些特殊,對于[-5, 256]區(qū)間內(nèi)的small_ints矗烛,python會進(jìn)行緩存辅柴,不會創(chuàng)建新的對象。

>>> a = 256
>>> b = 256
>>> a is b
True
>>> a == b
True
>>> a = 257
>>> b = 257
>>> a is b
False
>>> a == b
True

????字符串使用is的結(jié)果也不一定是相同瞭吃。

>>> c = 'abc.com'
>>> b = 'abc.com'
>>> c is b
False
>>> c == b
True
>>> c = 'efg'
>>> d = 'efg'
>>> c is d
True
>>> c == d
True

????tuple碌嘀,list,set的is==的結(jié)果都不一樣歪架。

>>> a = (1, 2, 3)
>>> b = (1, 2, 3)
>>> a == b
True
>>> a is b
False
>>> a = [1, 2, 3]
>>> b = [1, 2, 3]
>>> a == b
True
>>> a is b
False
>>> a = set([1, 2, 3])
>>> b = set([1, 2, 3])
>>> a == b
True
>>> a is b
False

dict Comprehensions

????創(chuàng)建dict有如下幾種方式股冗。

>>> 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
>>> DIAL_CODES = [
... (86, 'China'),
... (91, 'India'),
... (81, 'Japan'),
... ]
>>> country_code = {country: code for code, country in DIAL_CODES}
>>> country_code
{'China': 86, 'India': 91, 'Japan': 81}

處理缺失值

????同一個邏輯的幾種實現(xiàn)方式。

>>> my_dict = {}
>>> key = 'a'
>>> if key not in my_dict:
...     my_dict[key] = []
... 
>>> my_dict[key].append("value")
>>> my_dict
{'a': ['value']}
>>> my_dict = {}
>>> key = 'a'
>>> my_list = my_dict.get(key, [])
>>> my_list.append("value")
>>> my_dict[key] = my_list
>>> my_dict
{'a': ['value']}
>>> my_dict = {}
>>> key = 'a'
>>> my_list = my_dict.setdefault(key, [])
>>> my_list.append("value")
>>> my_dict
{'a': ['value']}
>>> my_dict = collections.defaultdict(list)
>>> key = 'a'
>>> my_dict[key].append("value")
>>> my_dict
defaultdict(<class 'list'>, {'a': ['value']})
>>> class ListDict(dict):
...     def __missing__(self, key):
...         return []
... 
>>> a = ListDict()
>>> my_list = a[key]
>>> my_list
[]
>>> my_list.append("value")
>>> a[key] = my_list
>>> a
{'a': ['value']}

????對于前三種方法和蚪,直接使用dict止状,setdefault會比前兩種方法更高效。如果需要更靈活的對缺失值的處理攒霹,可以采用第四種方法(default_dict)和第五種方法(繼承dict)怯疤。
????缺失值處理的實質(zhì)就是__missing__這個特殊方法的調(diào)用,這個方法只會在__getitem__(就是像d[k]這種形式)碰到缺失值時才會調(diào)用催束,而對get集峦,__contains__(in方法)等其他dict的方法沒有影響。所以在處理缺失值的時候,其他的方法也需要相應(yīng)的處理少梁。

其他類型的dict

collections.OrderedDict:按照key的插入順序維護(hù)keys洛口,可以以一個可預(yù)測的順序?qū)ey進(jìn)行遍歷。還可以popitem第一個或者最一個元素凯沪。
collections.ChainMap:把幾個dict放在一起進(jìn)行搜索第焰,搜索按照dict的順序進(jìn)行,在任意一個dict中找到key就會搜索成功妨马。比如在變量查找時挺举,可以先查找本地變量,再查找全局變量烘跺,最后查找系統(tǒng)變量湘纵。還有對命令行參數(shù),可以先查找用戶輸入的變量滤淳,再查找變量的默認(rèn)值梧喷。
collections.Counter:對每個key維護(hù)一個計數(shù),可以通過update方法更新一個key增加計數(shù)脖咐,還有most_common返回出行次數(shù)最多的n個key铺敌。

Subclassing UserDict

????一般創(chuàng)建一個新的mapping類型通過繼承UserDict會比繼承dict要容易。主要原因在于built-in對象的一些實現(xiàn)會走捷徑屁擅,導(dǎo)致你必須去重載方法偿凭,而繼承UserDict不會有這些問題。具體的原因還會在后續(xù)的章節(jié)詳細(xì)講解派歌。

Immutable Mappings

????MappingProxyType提供了一個原始mapping的動態(tài)識圖弯囊,意味著原始的mapping的更新能夠在這個mappingproxy中看到,但是不能通過他對原始的mapping進(jìn)行更新胶果。

>>> from types import MappingProxyType
>>> d={1:'A'}
>>> d_proxy = MappingProxyType(d)
>>> d_proxy
mappingproxy({1: 'A'})
>>> d_proxy[1]
'A'
>>> d_proxy[2] = 'x'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'mappingproxy' object does not support item assignment
>>> d[2] = 'B'
>>> d_proxy
mappingproxy({1: 'A', 2: 'B'})
>>> d_proxy[2]
'B'

Set

????set是一個唯一對象的集合匾嘱,可以用于去重。set存儲的元素必須是hashable的早抠,但是set本身不是hashable的奄毡,但frozenset是hashable的。

>>> l = ['spam', 'spam', 'eggs', 'spam']
>>> set(l)
{'spam', 'eggs'}
>>> hash(set(l))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'set'

????set提供一些中綴操作符贝或,如a | b表示union,a & b表示intersection锐秦,a - b表示difference咪奖。
????創(chuàng)建set使用{},但是要注意一個空的集合需要使用set()來創(chuàng)建酱床,否則創(chuàng)建出來的是一個dict羊赵。

>>> s={1}
>>> type(s)
<class 'set'>
>>> s={}
>>> type(s)
<class 'dict'>
>>> s = {i for i in range(10)}
>>> s
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
>>> type(s)
<class 'set'>

dict and set Under the Hood

????核心就是hash table,也是為什么dict和set的元素都需要是hashable的。不過和java相比昧捷,python解決hash沖突使用的是開放尋址法闲昭,而java使用的是鏈接法。使用開放尋址法導(dǎo)致內(nèi)存開銷較大靡挥,一旦無法解決沖突序矩,就需要擴充內(nèi)存,并且進(jìn)行元素的重新分布跋破。不過適合于key的快速查找簸淀。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市毒返,隨后出現(xiàn)的幾起案子租幕,更是在濱河造成了極大的恐慌,老刑警劉巖拧簸,帶你破解...
    沈念sama閱讀 211,348評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件劲绪,死亡現(xiàn)場離奇詭異,居然都是意外死亡盆赤,警方通過查閱死者的電腦和手機贾富,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,122評論 2 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來弟劲,“玉大人祷安,你說我怎么就攤上這事⊥闷颍” “怎么了汇鞭?”我有些...
    開封第一講書人閱讀 156,936評論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長庸追。 經(jīng)常有香客問我霍骄,道長,這世上最難降的妖魔是什么淡溯? 我笑而不...
    開封第一講書人閱讀 56,427評論 1 283
  • 正文 為了忘掉前任读整,我火速辦了婚禮,結(jié)果婚禮上咱娶,老公的妹妹穿的比我還像新娘米间。我一直安慰自己,他們只是感情好膘侮,可當(dāng)我...
    茶點故事閱讀 65,467評論 6 385
  • 文/花漫 我一把揭開白布屈糊。 她就那樣靜靜地躺著,像睡著了一般琼了。 火紅的嫁衣襯著肌膚如雪逻锐。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,785評論 1 290
  • 那天,我揣著相機與錄音昧诱,去河邊找鬼晓淀。 笑死,一個胖子當(dāng)著我的面吹牛盏档,可吹牛的內(nèi)容都是我干的凶掰。 我是一名探鬼主播,決...
    沈念sama閱讀 38,931評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼妆丘,長吁一口氣:“原來是場噩夢啊……” “哼锄俄!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起勺拣,我...
    開封第一講書人閱讀 37,696評論 0 266
  • 序言:老撾萬榮一對情侶失蹤奶赠,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后药有,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體毅戈,經(jīng)...
    沈念sama閱讀 44,141評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,483評論 2 327
  • 正文 我和宋清朗相戀三年愤惰,在試婚紗的時候發(fā)現(xiàn)自己被綠了苇经。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,625評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡宦言,死狀恐怖扇单,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情奠旺,我是刑警寧澤蜘澜,帶...
    沈念sama閱讀 34,291評論 4 329
  • 正文 年R本政府宣布,位于F島的核電站响疚,受9級特大地震影響鄙信,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜忿晕,卻給世界環(huán)境...
    茶點故事閱讀 39,892評論 3 312
  • 文/蒙蒙 一装诡、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧践盼,春花似錦鸦采、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至谅河,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背绷耍。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工吐限, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人褂始。 一個月前我還...
    沈念sama閱讀 46,324評論 2 360
  • 正文 我出身青樓诸典,卻偏偏與公主長得像,于是被迫代替她去往敵國和親崎苗。 傳聞我的和親對象是個殘疾皇子狐粱,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,492評論 2 348

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