可散列類型html
泛映射類型python
字典git
(1)字典推導式github
(2)處理不存在的鍵算法
(3)字典的變種app
集合dom
映射的再討論函數
python高級——目錄post
文中代碼均放在github上:https://github.com/ampeeg/cnblogs/tree/master/python高級測試
''' 可散列數據類型(也稱可hash)————我理解"可散列"就是"可hash" 可hash的對象須要實現__hash__方法,返回hash值;另外爲了與其餘對象比較還須要有__eq__方法 原子不可變數據類型(str、bytes和數值類型)都是可散列的,可散列對象必須知足下列要求: (1)實現了__hash__方法,而且所獲得的hash值是不變的 (2)實現了__eq__方法,用來比較 (3)若a == b 爲真,那麼hash(a) == hash(b)也是真 ''' # 建立類Foo,並實現__hash__和__eq__ class Foo: def __init__(self, name): self.name = name def __hash__(self): print("正在hash...") return hash(self.name) def __eq__(self, other): print("正在比較...") return self.name == other.name def __repr__(self): return self.name if __name__ == "__main__": f1 = Foo("小李") f2 = Foo("小紅") f3 = Foo("小李") s = set([f1, f2, f3]) # 集合實現不重複的原理正好利用了散列表 print(s) # {小紅, 小李} print( f1 == f3, hash(f1) == hash(f3)) # True True 知足可散列對象的第三個條件 |
''' 對於元組來講,只有當一個元組包含的全部元素都是可hash的狀況下,它纔是可hash的 ''' t1 = (1, 2, 3, [1, 2]) # 元組裏的列表的值是可變的,因此不可hash try: print(hash(t1)) except Exception as e: print(e) # unhashable type: 'list' t2 = (1, 2, 3, (1, 2)) # 元組裏的元素都是不可變的,而且第二層元組裏面的元素也不可變,因此可hash print(hash(t2)) # 3896079550788208169 t3 = (1, 2, 3, frozenset([1, 2])) print(hash(t3)) # -5691000848003037416 |
''' 泛映射類型就是廣義上的對應關係,在數學中,咱們將集合A對應集合B中的對應法則稱爲"映射"(Mapping) 一樣,在python裏,咱們稱"鍵值對"爲映射,這其實也是一種對應法則 若是一個數據類型是映射,那麼它確定屬於collections.abc.Mapping,可以使用isinstance函數測試 PS: 字典是 Python 語言中惟一的映射類型。映射類型對象裏哈希值(鍵) 和指向的對象(值)是一對多的關係。 ''' from collections import abc # 咱們測試一些經常使用的類型是否是映射 if __name__ == "__main__": print(isinstance({}, abc.Mapping)) # True 字典是典型的鍵值對 print(isinstance([1, 2], abc.Mapping)) # False 列表是序列 print(isinstance((1, 2), abc.Mapping)) # False 元組是序列 print(isinstance('adfasfd', abc.Mapping)) # False 字符串也是序列 |
''' 你們能夠查看_collections_abc.py源代碼,裏面基本的類型包含: ["Awaitable", "Coroutine", "AsyncIterable", "AsyncIterator", "Hashable", "Iterable", "Iterator", "Generator", "Sized", "Container", "Callable", "Set", "MutableSet", "Mapping", "MutableMapping", "MappingView", "KeysView", "ItemsView", "ValuesView", "Sequence", "MutableSequence", "ByteString", ] ''' |
''' 若是咱們本身想定義一個映射類型的對象,那麼必須實現__getitem__、__iter__、__len__方法 PS:關於該部分的原理,本人暫未查看說明文檔,畢竟現實中幾乎不可能自定義映射;有興趣的同志可深刻鑽研。 ''' class Foo(abc.Mapping): def __init__(self, name): self.name = name def __getitem__(self, item): return self.name def __iter__(self): return iter(str(self.name)) def __len__(self): return len(self.name) print(isinstance(Foo("123"), abc.Mapping)) # True |
''' 字典是python內置類型中惟一的映射,先看建立字典的幾種方法 一、對象建立 二、大括號 三、zip ''' if __name__ == "__main__": # 一、利用實例化對象的方法建立 a = dict(key1=1, key2=2, all=[1, 2, 3]) b = dict([('key3', 3), ('key4', 4)]) c = dict({"key5": 5, "key6": 6}) print("a:", a) # a: {'key1': 1, 'all': [1, 2, 3], 'key2': 2} print("b:", b) # b: {'key3': 3, 'key4': 4} print("c:", c) # c: {'key6': 6, 'key5': 5} # 二、直接使用大括號 d = {"key7": 7, "key8": 8} print("d:", d) # d: {'key8': 8, 'key7': 7} # 三、使用zip e = dict(zip(("key9", "key10", "key11"), [9, 10, 11])) print("e:", e) # e: {'key11': 11, 'key10': 10, 'key9': 9} |
(1)字典推導式
''' 字典推導式:字典推導式的建立方法同列表推導式相似 如下直接引用《流暢的python》中的例子 ''' if __name__ == "__main__": DIAL_CODES = [ (86, 'China'), (91, 'India'), (1, 'United States'), (62, 'Indonesia'), (55, 'Brazil'), (92, 'Pakistan'), (880, 'Bangladesh'), (234, 'Nigeria'), (7, 'Russia'), (81, 'Japan'), ] country_code = {country: code for code, country in DIAL_CODES} print(country_code) # {'Russia': 7, 'Indonesia': 62, 'Brazil': 55, 'China': 86, 'India': 91, 'Bangladesh': 880, 'Pakistan': 92, 'United States': 1, 'Nigeria': 234, 'Japan': 81} code_upper = {code: country.upper() for country, code in country_code.items() if code < 66} print(code_upper) # {1: 'UNITED STATES', 7: 'RUSSIA', 62: 'INDONESIA', 55: 'BRAZIL'} |
(2)處理不存在的鍵
''' 處理找不到的鍵 在實際場景中,當使用d[key]的方法查找數據的時候,若是找不到該鍵,python會拋出KeyError異常; 若是是取值操做,可使用d.get(key, default)來解決,能夠給找不到的鍵一個默認的值 可是若是要給更新某個不存在鍵對應的值的時候,就稍顯麻煩了,可使用如下方法解決: 一、用setdefault處理dict找不到的鍵 二、使用defaultdict對象 三、__missing__方法 ''' class Foo: def __init__(self, name=None): self.name = name def __repr__(self): return str(self.name) def setattr(self, key, value): self.__setattr__(key, value) return self if __name__ == "__main__": d1 = {} print(d1.get("key", "default")) # default 使用d.get(key, default)的方法取值 # 一、用setdefault處理dict找不到的鍵 d2 = {} d2.setdefault("key", [x for x in "adfaf"]) # setdefault雖然是set名字,可是是取值操做,只有當鍵不存在時才進行賦值,並返回該值 l = d2.setdefault("key", []) print(l) # ['a', 'd', 'f', 'a', 'f'] d2.setdefault("key2", []).extend([1, 2, 3]) # 返回空列表,因此可在後面直接使用方法extend print(d2) # {'key': 'default', 'key2': [1, 2, 3]} # 二、使用defaultdict對象 # 在python中,還有一些dict的變種類型,defaultdict爲其中一種,位於collections中 from collections import defaultdict dic = defaultdict(list) # 將list的構造方法做爲default_factory(只有__getitem__找不到值時調用) dic["key"].extend([1, 2, 3]) # dic中不含有"key"鍵,此時default_factory會被調用,創造一個空列表,並鏈接[1, 2, 3] print(dic["key"]) # [1, 2, 3] dic = defaultdict(Foo) # 將Foo的構造方法做爲default_factory建立一個defaultdict print(dic["key"].setattr("name", "default")) # default # 三、__missing__方法 # 全部的映射類型在找不到鍵的時候,都會牽扯到__missing__方法;若是在__getitem__找不到鍵的時候,python就會自動調用它 # 另外,__missing__方法只會被getitem調用,對get或者__contains__沒有影響 class My_dict(dict): def __missing__(self, key): print("正在調用__missing__...") mdict = My_dict(one=1, two=2, three=3) print(mdict) # {'two': 2, 'three': 3, 'one': 1} mdict["key"] # 正在調用__missing__... |
(3)字典的變種
''' 在python中雖然只有dict爲映射類型,可是dict有不少變種,上面defaultdict就是,除此以外還有: (1)OrderedDict: 有順序的字典 (2) ChainMap: 能夠容納數個不一樣的映射對象 (3) Counter: 給鍵準備一個整數計數器,每次更新鍵的時候會增長該計數器 (4)UserDict: 將標準的dict用python實現了一遍 ''' from collections import OrderedDict, ChainMap, Counter, UserDict if __name__ == "__main__": # 一、OrderedDict d = OrderedDict() d['one'] = 1 d['two'] = 2 d['three'] = 3 for _ in range(10): print("%d次:" % _) for k, v in d.items(): print("**", k, v) # OrderedDict迭代的時候的順序老是跟插入順序一致 # 二、ChainMap pylookup = ChainMap(d, globals()) # d和globals()都是映射類型,ChainMap會將其組合 for v, k in pylookup.items(): print(v, k) # 三、Counter ct = Counter('asfjlajslfjals') print(ct) # Counter({'j': 3, 'l': 3, 's': 3, 'a': 3, 'f': 2}) # 存儲的是每一個字母出現的次數 ct.update('jjjjjjjjlllllllll') print(ct) # # Counter({'l': 12, 'j': 11, 's': 3, 'a': 3, 'f': 2}) import random ct2 = Counter([random.randrange(1, 5) for _ in range(100)]) # 列表推導式建立Counter print(ct2) # Counter({1: 30, 2: 24, 4: 24, 3: 22}) ct3 = Counter((random.randrange(1, 5) for _ in range(100))) # 生成器建立Counter print(ct3) # Counter({2: 40, 3: 23, 4: 20, 1: 17}) class Foo: def __init__(self, num): self.l = [random.randrange(1, 5) for _ in range(num)] def __iter__(self): return iter(self.l) ct4 = Counter(Foo(100)) # 可迭代對象建立Counter print(ct4) # Counter({2: 31, 3: 25, 4: 25, 1: 19}) # 四、UserDict # 建立自定義的映射類型,通常以UserDict爲基類 class My_dict(UserDict): def __missing__(self, key): if isinstance(key, str): raise KeyError(key) return self[str(key)] def __contains__(self, key): return str(key) in self.data def __setitem__(self, key, item): print("調用__setitem__。。。") self.data[str(key)] = item mdict = My_dict() mdict["one"] = 1 # 調用__setitem__。。。(下同) mdict["two"] = 2 mdict["three"] = 3 print(mdict) # {'three': 3, 'one': 1, 'two': 2} |
''' 集合對於不少人並不陌生,中學階段就已經接觸過。集合具備: (1)肯定性:每個對象都能肯定是否是某一集合的元素,沒有肯定性就不能成爲集合 (2)互異性:集合中任意兩個元素都是不一樣的對象 (3)無序性:{a,b,c}{c,b,a}是同一個集合 在python中,set中的元素必須是可散列的,但set自己不可散列(可是frosenset是可散列的) 另外:set實現了不少基礎運算 &(交集)、|(並集)、-(差集) ''' if __name__ == "__main__": # 建立集合 s1 = set([1, 2, 3]) s2 = {1, 2, 3, 4} print(s1, s2) # {1, 2, 3} {1, 2, 3, 4} # 集合推導式 s3 = {x**2 for x in range(10)} print(s3) # {0, 1, 64, 4, 36, 9, 16, 49, 81, 25} |
set的操做方法不少,本文截自<流暢的python>一書,以下三個表:
表一:集合的數學方法
表2:集合的比較運算
表3:集合的其餘運算
''' python標準庫裏面的映射類型都是可變的,有時候須要使用不可變的映射,從python3.3開始,types模塊中引入了 MappingProxyType類,若是給這個類一個映射,那麼它會返回這個映射的試圖,該試圖是動態的,原映射若是有改動 可當即經過這個試圖觀察到,可是這個試圖沒法對該映射進行修改。 ''' from types import MappingProxyType if __name__ == "__main__": d = {'one':1, 'two':2, 'three':3} d_proxy = MappingProxyType(d) print(d_proxy) # {'three': 3, 'two': 2, 'one': 1} print(d_proxy['one']) # 1 for k, v in d_proxy.items(): print(k, v) #d_proxy['four'] = 4 # 報錯:TypeError: 'mappingproxy' object does not support item assignment d['four'] = 4 print(d_proxy) # {'two': 2, 'three': 3, 'four': 4, 'one': 1} |
另外,《流暢的python》77頁到80頁對散列表算法以及字典、集合的效率、平時須要注意的問題進行了比較詳細的探討,建議嚴謹並有興趣的同仁閱讀,該部份內容對理解字典類型無比有益,場景中捉摸不透的莫名其妙的bug可能會迎刃而解。
重要的結論摘錄以下:
(1)鍵必須是可散列的
(2)字典在內存上的開銷巨大
(3)鍵查詢很快
(4)鍵的次序取決於添加順序
(5)往字典裏添加新鍵可能會改變已有鍵的順序