Python進階筆記(四)深入Python的set和dict

4.1 dict的abc繼承關係

dict是屬於Mapping類型

from collections.abc import Mapping, MutableMapping
# a實際上並不是去繼承了MutableMapping,其只是去實現了MutableMapping中的一些魔法函數;
a = {}
print (isinstance(a, MutableMapping))

4.2 dict的常用方法

同樣,我們通過a = dict()來查看dict的源碼:

a = {"bobby1":{"company":"imooc"},
     "bobby2": {"company": "imooc2"}
     }
#clear:清空dict
# a.clear()
# pass

#copy, 返回淺拷貝
new_dict = a.copy()
new_dict["bobby1"]["company"] = "imooc3"
print(f'new_dict:{new_dict}')
print(f'dict:{a}')

注意這裏的淺拷貝,如果按照上述修改的話,new_dict是多少,a又是多少?
淺拷貝不理解的可以參考筆者之前的文章:深入理解賦值,淺拷貝,深拷貝
那麼,當我們對a淺拷貝時,bobby1是拷貝過去了,bobby2也確實拷貝過去了,但是bobby1,2的value仍然是一個字典,故其value指向的引用,仍然是之前的引用,所以對淺拷貝的值更換,原來的值也依然會變化。
結果爲:
new_dict:{‘bobby1’: {‘company’: ‘imooc3’}, ‘bobby2’: {‘company’: ‘imooc2’}}
dict:{‘bobby1’: {‘company’: ‘imooc3’}, ‘bobby2’: {‘company’: ‘imooc2’}}
如果是這樣,則淺拷貝的值將不會變化,如下:

a = {"bobby1":"company1",
     "bobby2":"company"}
new_dict = a.copy()
new_dict["bobby1"] = "imooc3"
print(f'new_dict:{new_dict}')
print(f'dict:{a}')
--------------------------------
new_dict:{'bobby1': 'imooc3', 'bobby2': 'company'}
dict:{'bobby1': 'company1', 'bobby2': 'company'}

那麼如何做深拷貝呢,dict的類中沒有實現,我們需要用copy的包,可以參考文章,這裏不再詳細敘述。

接着看fromkeys方法:
在這裏插入圖片描述
可以看到,fromkeys是一個靜態方法,其說明表示它返回的是一個由可迭代對象默認值組合成的新的字典

#formkeys
new_list = ["bobby1", "bobby2"]

new_dict = dict.fromkeys(new_list, {"company":"imooc"})
print(f'new_dict:{new_dict}')
------------------------------------------------
new_dict:{'bobby1': {'company': 'imooc'}, 'bobby2': {'company': 'imooc'}}

get()方法:
在這裏插入圖片描述
當沒有key時,會出現key-error錯誤,而get方法就是爲了避免這種錯誤出現,如果沒有則返回爲空,或者可以自定義返回值。當然在實際編程中,通常我們自己定義的字典類不用get取值,而取json中的數據時才用get,詳細可參考:Pythonic總結

value =  new_dict.get("bobby1",{})
print(f'value:{value}')

items方法:
常見方法,通常用來遍歷字典:

for key,value in new_dict.items():
	print(key,value)

setdefault方法:
在這裏插入圖片描述
get的擴展版,當key的返回值爲空時,不僅支持返回值,而且還可以對當前的空值進行填充;如果key有對應的值,則直接返回key原來對應的值,對key值不做改變。

default_value = new_dict.setdefault('bobby','imooc')
print(default_value)

update方法:
在這裏插入圖片描述
其可以將iterable對象合併,注意update無返回值;

new_dict.update({"bobby":"imooc"})
print(new_dict)
new_dict.update(bobby3="bobby3")
print(new_dict)
# 還可以支持list裏放tuple的形式
new_dict.update([("bobby","imooc")])

4.3 dict的子類

不建議繼承list和dict類型,因爲在某些情況下,內置dict(內部是c語言書寫的)其不會去調用我們覆蓋的方法,如下:

#不建議繼承list和dict
class Mydict(dict):
    def __setitem__(self, key, value):
        super().__setitem__(key, value*2)
# 沒有使用我們自定義的方法來創建字典
my_dict = Mydict(one=1)
print (my_dict)
# 使用了我們自定義的方法來創建字典
my_dict["one"] = 1
print (my_dict)
--------------------------------------
{'one': 1}
{'one': 2}

那如果要去繼承dict怎麼辦呢?可以用collection模塊的子類UserDict拿來繼承

from collections import UserDict
class Mydict(UserDict):
	def __setitem__(self,key,value):
		super().__setitem__(key,value*2)
my_dict = Mydict(one=1)
print (my_dict)
----------------------------------------------
{'one': 2}

python有一個內置的繼承子類叫defaultdict,在Userdict中有一個missing方法:
在這裏插入圖片描述
當我們找不到key時,就會判斷有沒有missing這個魔法函數,而defaultdict其實就是重寫了missing方法,如下:
在這裏插入圖片描述
missing方法當某個值找不到時,就會通過default_factory方法將某個值設置進來:
在這裏插入圖片描述

from collections import defaultdict

my_dict = defaultdict(dict)
# 當沒有bobby這個key時
my_value = my_dict["bobby"]
print(my_value)

(持續更新,內部機理還會擴充。。。)

4.4 set和frozenset

set 是集合,frozenset是不可變集合,set是一個無序的集合,其不重複,在去重時用的非常多。
set的其他用法也可以參考:Pythonic總結
在這裏插入圖片描述
查看set,其接受的是一個iterable對象,即可迭代對象。所以我們用數組,字符串,列表都是可以的。

# set的結果是不重複且無序的
s = set(['a','b','c','d','e'])
print(s)
--------------------
{'d', 'e', 'c', 'a', 'b'}
# set可以添加值,而frozenset則是不可變的類型
s = {'a','b','c'}
s.add('c')
print(s)
# frozenset定義的值是不可變的,其也不可以再add添加
s = frozenset("abcde")   # frozenset 可以作爲dict的key


# 向set添加數據
another_set = set("def")
s.add('c')
s,update(another_set)
print(s)

#  集合的運算
another_set = set("cef")
# 差集:s - another_set: 是作爲一個返回值返回,而不改變之前的s
re_set = s.difference(another_set)
re_set = s - another_set
#  交集
re_set = s & another_set
# 並集
re_set = s | another_set
print(re_set)

# set性能很高
# 判斷某個元素是否在集合當中
if "c" in re_set:
	print ("i am in set")
# 因爲其實現了__contains__的魔法函數,所以其可以做in的判斷;

# 判斷集合是不是另一集合的一部分
print (s.issubset(re_set))

4.5 dict和set的實現原理

我們通過同一個文件中找尋字符串的速率來比較dict,list和set的性能(過程略):

dict查找的性能要遠遠大於list,在list中隨着list數據的增大,查找時間會增大,在dict中查找元素不會隨着sict的增大而增大。

dict背後的實現原理其實是散列表(hash表),有一段連續的數組,數組中存儲了指向key和value的指針。我們要存一個值到我們的哈希中,其會首先由hash()函數計算我們的數值,這個數值可以理解爲一個偏移量,即將key和value放在偏移值的位置上。如果有衝突的情況,則採用某種規則進行順移。
數組相比鏈表最大的優點就是可以根據偏移量來直接獲取值,做到任何一個位置直接獲取。
在這裏插入圖片描述
字典dict中的哈希表查找過程:
在這裏插入圖片描述

總結:

1. dict的key或者set的值 都必須是可以hash的,不可變對象都是可hash的, str, fronzenset, tuple,自己實現的類修改 hash
2. dict的內存花銷大(會由空餘的表元),但是查詢速度快, 自定義的對象 或者python內部的對象都是用dict包裝的;
3. dict的存儲順序和元素添加順序有關
4. 添加數據有可能改變已有數據的順序
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章