PythonI/O進階學習筆記_5.python的set和dict
前言:
我一直覺得對我來說學習知識很忌諱不繫統。本篇內容與上一篇 自定義序列類是有聯絡的。
上一篇比較通範的瞭解了序列類的一些協議和特性,並且有些list的內容。這篇更加具體到set和dict這兩個序列類。
以此來了解python序列類的具體應用。(這篇比較簡單)(感覺具體比抽象都更容易理解,但是也要學會思考把具體物件抽象化來表達,即提取共性)
content:
1.dict在abc中的序列型別和繼承關係
2.dict實現了的常用方法
3.我可不可以繼承dict這種序列類?
4.set和frozenset
5.set和dict的原理
==============
1.dict在abc中的序列型別和繼承關係
dict在collection.abc中,實際上是屬於MutableMapping(可變mapping)型別。
跟上篇對可變序列類繼承的分析一樣,MutableMapping繼承了Mapping的一些功能並且加了一些可變的特性,
Mapping繼承了Collection。接下來的繼承和上篇的一樣。
2.dict實現了的常用方法
如果用的是pycharm,還是用ctrl+b就能跳到python對dict的定義。
常用:
a = {"1":{"a":"aa"}, "2":{"b":"bb"}} # 清空字典 a.clear() # 淺拷貝字典 淺拷貝雖然可以正常賦值,但是如果 my_dopy_dict 中的值進行了改變,則 a 中的值也會進行對應的改變 my_dopy_dict = a.copy() # 深拷貝 深拷貝則是實實在在的在記憶體當中聲明瞭一個新的變數 import copy new_dict = copy.deepcopy(a) # get函式 dict.get(要查詢的key,如果沒找到對應key的內容返回的資料) print(a.get("3",{1:"3"})) # {1: '3'} # dict.fromkeys() 函式用於建立一個新字典,以序列 seq 中元素做字典的鍵 seq可以是可迭代的,value 為字典所有鍵對應的初始值。 my_list = [1, 2, 3] my_new_dict = dict.fromkeys(my_list, {"222":"3434"}) #{1: {'222': '3434'}, 2: {'222': '3434'}, 3: {'222': '3434'}} # setdefault() 函式和 get()方法 類似, # 如果鍵不存在於字典中,將會新增鍵並將值設為預設值。 # 如果存在,則將會返回該key對應的value a.setdefault("3", "cc") # a= {'1': {'a': 'aa'}, '2': {'b': 'bb'}, '3': 'cc'} print(a.setdefault("2", "cc")) # 返回{'b': 'bb'} # update() 函式把字典dict2的鍵/值對更新到dict裡。 # 如果字典b中有與a相同的key,則會把a中的key對應的value進行更新 # 如果字典b中有a中沒有的key,則a會將未有的key與value新增進去 b = {"3": "cc", "2": "dd"} a.update(b) print(a) # {'1': {'a': 'aa'}, '2': 'dd', '3': 'cc'}
3.我可不可以繼承dict這種序列類?(dict的子類)
a.如果我偷懶想實現dict這種型別,能不能直接繼承這種序列類呢?同理list是否可以?
例:繼承dict,並且重寫設定dict key的value時呼叫的魔法函式,使其值變為2倍
class Mydict(dict): def __setitem__(self, key, value): super().__setitem__(key, value*2) a=Mydict(b=1) print(a) a['b']=1 print(a)
輸出:
可以發現,原來同樣功能和效果的,我們重寫方法後,第一種方法去設定key的value值這一操作並沒有呼叫我們重寫的方法。
所以並不建議去繼承python的這種序列類。
b.有沒有什麼辦法我實在想繼承?
python裡專門給了個UserDict類,可以實現想要的繼承Dict類的效果
from collections import UserDict class Mydict(UserDict): def __setitem__(self, key, value): super().__setitem__(key, value*2) mydict = Mydict(one = 1) # {'one': 2} 呼叫__setitem__這個魔法函式 mydict["one"] = 2 # {'one': 4} 這種方式也可以呼叫__setitem__
輸出:
c.python中Dcit實際也有子類實現:defaultdict
使用:
from collections import defaultdict # 這個是dict的子類 mydict = defaultdict(dict) myvalue = mydict["bai"] # 如果不存在的話,返回{}
輸出:
4.set和frozenset
a.兩者是啥有啥特點?
set:集合(無序,不重複,可變)
frozenset:不可變集合(無序,不重複,不可變)
frozenset沒有改變的方法 一旦初始化了 就不可變了。其中,不可變型別對可變型別的一個特點,不可變型別是可以作為dict的key的。 b.set常用操作a=set('abcdee') a.add('f') print(a) another_set=set('defgh') #新增資料 #a.update(another_set) #print(a) #集合的差集 re_set=a.difference(another_set) #減法實現於__ior__魔法函式 re_set2=a-another_set #集合的交集& re_set3=a&another_set #集合的並集| re_set4=a|another_set print(re_set) print(re_set2) print(re_set3) print(re_set4) #也可以用if in判斷(實現於__contains__魔法函式) if 'a' in re_set: print('I am a set')
5.set和dict的原理
之前就提過,set的效能棒的。dict的查詢效能遠比起list要好。
並且list中隨著list資料的增大,查詢時間會增大,而dict不會。
這是為什麼呢?
因為dict使用hash這種資料結構儲存。set也是。
a.dict的散列表
特點:
- dict的key必須是可hash的,不可hash的是不能當成dict的key的,比如list - python申請記憶體的時候,會初始化一個小的連續的空空間 - 由上一條可得這樣 一定會存在浪費,這時候每次變動操作 會計算剩餘空間,剩餘空間小於1/3的時候,會重新生成空間並且將現在資料都拷貝過去(rehash) - 陣列比這種連結串列結構好的地方,就是可以直接根據偏移量來存取,而不用全部開始從頭遍歷。 (這種結構hash和重hash的策略用在很多地方,包括redis等) b.儲存結構瞭解了,那麼資料的查詢過程呢?先計算a的雜湊值,查詢表源是否為空,
因為a是不變的,所以如果表源為空,那麼就會丟擲key error。
如果表源不為空,也有可能是其他key,檢視key是否是要查詢的key。
如果是其他key,重新雜湊迴圈查詢。
c.這種hash結構在ptthon中的特點
- 我們可以用__hash__這個魔法函式實現可hash物件
- dict記憶體開銷比較大,這是hash表的特點。
- 實際上python內部類和物件,都是dict。
- dict儲存順序和元素新增順序有關。
- 插入資料後檢查剩餘空間引發的重hash,會影響原來的資料(比如地址)。
&n