免費視頻教程!零基礎學Python系列(11) - 數據類型之字典

本節我們接着講字典(Dictionary)類型:

這個系列教程建議對照着視頻學習,以下僅爲課件內容。

本節課程的視頻和實例源碼下載方式:點擊->我的主頁,查看個人簡介。

我儘量堅持每日更新一節。


字典,也是Python中使用得比較廣泛的一種數據類型。

字典本質上是一種“鍵值對”(key-value)的集合。“鍵值對”這種數據的描述方式,更加符合我們對客觀世界的認識。客觀世界的數據,通常都存在一個名字-key,以及對應的值-value,使用“鍵值對”可以非常直觀簡便地表達這些數據。一些數據庫技術也是基於鍵值對的數據存儲方式,比如Redis、memcached。“鍵值對”在大數據領域也有廣泛應用。

字典的結構定義和JSON基本一致,兩者可以很方便的相互轉換。

 

字典的語法如下:

{key1: value1, key2: value2, key3: value3, …}

字典使用大括號{}括起來,每個key-value之間使用逗號分割。Key和value之間使用冒號:分割。

同一個字典裏面,key值需要保證唯一,不能重複,並且只能是不可變數據類型(數字、字符串、元組)。

Value不要求唯一,並且可以是任意數據類型。

 

  • 瞭解hash結構:

字典是一種hash結構,我們有必要簡單瞭解一下hash結構。下圖是一個hash結構的示意(這不是python的真正實現):

 

hash是一種數據結構,準確說是一種數據的組織方式。我們在編程中,爲了便於數據的操作(增刪改查),尤其是性能考慮,會將數據對象以某種特別的方式組織起來,這就是數據結構。除了hash以爲,還有數組array、鏈表sll\dll、平衡二叉樹avl、紅黑樹rbt等。這些數據結構,是編程的算法範疇,不是編程語言的語法範疇,不同的編程語言都可以實現這些數據結構。我們不會重點去講這些算法的東西,但是作爲程序員,你需要理解這些常用的算法。如果有必要我後面可以專門製作一個系列來講算法。

上圖是我工作中最常見的hash結構。它定義了一個hash表(核心是一個定長的數組),用於存hash_key,由於它是數組,所以可以通過索引來查找,性能高。數據對象的key,通過一個hash函數映射到hash_key,這個映射關係可能是n:1,所以存在一個hash_key對應多個數據對象的情況,這就是所謂的hash衝突。爲了解決hash衝突,我們在每個hash_key下面掛了一個鏈表結構,這個就是hash桶(bucket)。Hash衝突越嚴重,bucket就會越深,從而導致查找性能就越低,所以hash函數是關鍵。衡量一個hash函數的好壞,是它能否將所有數據對象均衡的散列到不同的hash_key上面的,並且hash函數本身不能耗費太多性能。

 

Hash結構,是各種操作(增刪改查)性能都比較高比較均衡的一種結構,使用較廣泛。

 

  • 字典和列表的區別

字典不是一種序列,它沒有索引,並且是無序的,這一點和列表存在本質區別,大家要注意。

實質上,字典在python的底層實現中,是一種hash索引結構。所以字典結構在查詢、新增、刪除、修改上面表現出更加優異的時間性能。

 

而列表在python的底層實現中,是一種動態數組結構。該數組元素不會直接保存列表元素的數據,而是存儲了列表元素的地址。地址的大小是固定的,所以它可以是一個數組結構。由於它是動態數組(支持resize內存空間),所以也可以支持擴容。同時,數組是連續存儲的,所以列表存在索引,是有序的。

下圖示意了字典和列表的區別,不同的python版本可能存在差異,但是大體類似。

 

列表採用了指針數組,它是一段連續內存,所以它可以像數組一樣去索引的。而字典採用了hash表結構,key值是通過hash函數去命中到一個hash index,所以它只能通過key值去查找。

真正的實現中,不會像我畫的這麼簡單。比如字典的hash表還需要考慮hash散列、hash衝突,但是這些細節並不影響我們理解字典和列表之間的本質區別。對於初學者,不建議大家現在去搞懂這些細節,可以留待後面再去深入理解。

 

下面我們通過一系列實例來熟悉字典的操作:


# file: ./5/5_8.py

# 字典
dict_1 = {'name': 'xiaowang', 'age': 20, 'city': 'Chengdu'}
print('name: %s, age: %d, city: %s' % (dict_1['name'], dict_1['age'], dict_1['city']))

dict_1['city'] = 'Beijing'
print('name: %s, age: %d, city: %s' % (dict_1['name'], dict_1['age'], dict_1['city']))

dict_2 = {}.fromkeys(('name', 'age', 'city'))  # 批量根據key值初始化一個字典
print(dict_2)

print(len(dict_1))

# 增加鍵值對
dict_1['sex'] = 'male'
print(dict_1)

# 刪除鍵值對
del dict_1['sex']
print(dict_1)

# 批量更新
dict_3 = {'name': 'xiaoliu', 'age': 18, 'city': 'Chengdu', 'sex': 'female'}
dict_1.update(dict_3)
print(dict_1)

# 判斷key是否在字典中存在
print('name' in dict_3)

# 獲取所有的key
print(list(dict_1.keys()))

# 獲取所有的value
print(list(dict_1.values()))

# 獲取所有的(鍵, 值) 元組列表
print(list(dict_1.items()))

# 清空字典
dict_1.clear()
print(dict_1)

 

  • 深複製和淺複製

python提供了copy()方法,用於字典的淺複製(shallow copy)。與淺複製相對應的,還有深複製(deep copy)的概念。我們通過實例來理解一下二者的區別。淺複製和深複製的概念在很多編程語言中都涉及,大家應該理解並舉一反三。

# file: ./5/5_10.py

# 深複製和淺複製
dict_1 = {'name': 'xiaowang', 'age': 20, 'score': {'yuwen': 90, 'shuxue': 100}}
dict_2 = dict_1.copy()

print('dict_1:', dict_1)
print('dict_2:', dict_2)

# 修改value
print('modify 90->98'.center(64, '-'))
dict_1['score']['yuwen'] = 98
print('dict_1:', dict_1)
print('dict_2:', dict_2)

# 深拷貝
print('deep copy'.center(64, '-'))

import copy
dict_3 = copy.deepcopy(dict_1)
print('dict_1:', dict_1)
print('dict_3:', dict_3)
print('modify 98->99'.center(64, '-'))
dict_1['score']['yuwen'] = 99
print('dict_1:', dict_1)
print('dict_3:', dict_3)

輸出結果爲:

dict_1: {'name': 'xiaowang', 'age': 20, 'score': {'yuwen': 90, 'shuxue': 100}}

dict_2: {'name': 'xiaowang', 'age': 20, 'score': {'yuwen': 90, 'shuxue': 100}}

-------------------------modify 90->98--------------------------

dict_1: {'name': 'xiaowang', 'age': 20, 'score': {'yuwen': 98, 'shuxue': 100}}

dict_2: {'name': 'xiaowang', 'age': 20, 'score': {'yuwen': 98, 'shuxue': 100}}

---------------------------deep copy----------------------------

dict_1: {'name': 'xiaowang', 'age': 20, 'score': {'yuwen': 98, 'shuxue': 100}}

dict_3: {'name': 'xiaowang', 'age': 20, 'score': {'yuwen': 98, 'shuxue': 100}}

-------------------------modify 98->99--------------------------

dict_1: {'name': 'xiaowang', 'age': 20, 'score': {'yuwen': 99, 'shuxue': 100}}

dict_3: {'name': 'xiaowang', 'age': 20, 'score': {'yuwen': 98, 'shuxue': 100}}

 

所謂淺複製,其實是複製了內存的地址,並沒有拷貝內存裏面存的數據。

而深複製,則是重新分配了一塊內存,並把數據拷貝進去。

所以,我們通過上面的實例可以看到,在淺複製的情況下,我們改變dict_1的值,dict_2依然會跟着變化,說明他們指向了同一塊內存空間。而深複製的情況下,則互不影響,說明他們指向的是不同的內存空間。

通過下面的圖,更加容易理解:

 

大家可以思考一下,爲什麼key爲‘name’和‘age’的值不受淺複製的影響呢?

 

下一節我們講集合Set的概念。


本節課程的視頻和實例源碼下載方式:點擊->我的主頁,查看個人簡介。

我儘量堅持每日更新一節。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章