1. 程式人生 > >PythonI/O進階學習筆記_5.python的set和dict

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