字典是Python中唯一內建的映射類型,字典指定值并沒有特殊順序,都存儲在一個特殊的鍵(Key)里,鍵可以是數字、字符串或元組。
字典是另一種可變容器模型,可存儲任意類型對象。
字典的使用
字典這個數據結構的功能就跟它的名字一樣,可以像《漢語字典》一樣使用。在使用漢語字典時,我們可以從頭到尾一頁一頁查找某個字,也可以通過拼音索引或筆畫索引快速找到某個字,在漢語字典中找拼音索引和筆畫索引非常輕松簡單。
在Python中對字典進行了構造,讓我們可以輕松查到某個特定的鍵(類似拼音或筆畫索引),從而通過鍵找到對應的值(類似具體某個字)。
假如需要創建一個可以存儲學生姓名和學號的小型數據庫,應該怎么做呢?我們嘗試使用之前學習過的列表實現,例如:
>>> students=['小萌','小智','小強','小張','小李']
>>> numbers=['1001','1002','1003','1004','1005']
>>> print('小智的學號是:',numbers[students.index('小智')])
小智的學號是: 1002
輸出結果是我們想要的,但是當數據量比較大時,顯然不適用。數據量比較大時,意味著要對學生姓名列表創建一個有同樣多元素的學號列表,一旦列表發生變更,就要將學號列表和學生姓名列表進行逐步比對,以進行相應變更。
我們想要的真正效果是像使用index方法一樣,index返回的是索引位置,我們希望直接返回索引位置上的值,比如:
>>> print('小智的學號是:',numbers['小智'])
小智的學號是: 1002
這種方式可以實現,如果numbers是字典,就可以這么操作并得到結果。
創建和使用字典
字典的創建格式如下:
>>> d = {key1 : value1, key2 : value2 }
字典由多個鍵及其對應的值構成的對組成(把鍵/值對稱為項)。字典的每個鍵/值(key/value)對用冒號(:)分割,每個項之間用逗號(,)分割,整個字典包括在花括號({})中。空字典(不包括任何項)由兩個大括號組成,如{}。
鍵必須是唯一的,但值不必。值可以取任何數據類型,鍵必須是不可變的,如字符串、數字或元組。
下面是一個簡單的字典示例:
>>> dict = {'小萌': '1001', '小智': '1002', '小強': '1003'}
也可以為如下形式:
>>> dict1 = { 'abc': 456 }
>>> dict2 = { 'abc': 123, 98.6: 37 }
1 dict函數
可以用dict函數,通過其他映射(如其他字典)或鍵/值序列對建立字典,例如:
>>> student=[('name','小萌'),('number','1001')]
>>> detail=dict(student)
>>> print('學生詳細信息:',detail)
學生詳細信息: {'name': '小萌', 'number': '1001'}
>>> print('學生姓名:',detail['name'])
學生姓名: 小萌
>>> print('學生學號:',detail['number'])
學生學號: 1001
由輸出結果看到,dict函數可以將序列轉換為字典。字典的操作很簡單,在5.1節期望的功能已經實現了。
dict函數可以通過關鍵字參數創建字典,例如:
>>> detail=dict(name='小智',number='1002')
>>> print('學生詳細信息:',detail)
學生詳細信息: {'name': '小智', 'number': '1002'}
由輸出結果看到,通過關鍵字參數創建了字典。
通過關鍵字創建字典功能是dict函數非常有用的一個功能,可以多加運用。
2 字典的基本操作
字典的基本操作在很多方面與序列(sequence)類似,支持修改、刪除等操作。下面進行具體的講解。
1. 修改字典
向字典添加新內容的方法是增加新鍵/值對,修改或刪除已有鍵/值對,例如:
>>> student={'小萌':'1001','小智':'1002','小強':'1003'}
>>> student['小強']='1005' #更新小強的學號
>>> print('小強的學號是:%(小強)s' % student)
小強的學號是:1005
>>> student['小張']='1006' #添加一個學生
>>> print('小張的學號是:%(小張)s' % student)
小張的學號是:1006
由輸出結果看到,修改和添加均操作成功。
2. 刪除字典元素
此處的刪除指的是顯式刪除,顯式刪除一個字典用del命令,例如:
>>> student={'小強': '1005', '小萌': '1001', '小智': '1002', '小張': '1006'}
>>> print('刪除前:',student)
刪除前: {'小強': '1005', '小萌': '1001', '小智': '1002', '小張': '1006'}
>>> del student['小張'] #刪除鍵“小張”
>>> print('刪除后:',student)
刪除后: {'小強': '1005', '小萌': '1001', '小智': '1002'}
由輸出結果看到對應鍵/值被正確刪除了。
除了刪除鍵,還可以刪除整個字典,例如:
>>> student={'小強': '1005', '小萌': '1001', '小智': '1002', '小張': '1006'}
>>> print('刪除前:',student)
刪除前: {'小強': '1005', '小萌': '1001', '小智': '1002', '小張': '1006'}
>>> del student #刪除字典
>>> print('刪除后:',student)
Traceback (most recent call last):
File "<pyshell#70>", line 1, in <module>
print('刪除后:',student)
NameError: name 'student' is not defined
由輸出結果看到,刪除字典后就不能對字典進行訪問了,因為執行del操作后字典就不存在了,因而會報變量沒有定義的錯誤。
3. 字典鍵的特性
字典值可以沒有限制地取任何Python對象,既可以是標準對象,也可以是用戶定義的對象,但鍵不行。
需要記住以下兩點:
(1)不允許同一個鍵出現兩次。創建時如果同一個鍵被賦值兩次,后面的值會被記住,例如:
>>> student={'小萌': '1001', '小智': '1002', '小萌': '1005'}
>>> print('學生信息:',student)
學生信息: {'小萌': '1005', '小智': '1002'}
由輸出結果看到,一個鍵被賦值兩次,后面的值會被記住。
(2)鍵必須不可變,可以用數字、字符串或元組充當,不能用列表,例如:
>>> field={['name']:'小萌','number':'1001'}
Traceback (most recent call last):
File "<pyshell#80>", line 1, in <module>
field={['name']:'小萌','number':'1001'}
TypeError: unhashable type: 'list'
由輸出結果看到,不能用列表做鍵,會提示類型錯誤。
4. len函數
len(dict)函數用于計算字典元素的個數,即鍵的總數,例如:
>>> student={'小萌': '1001', '小智': '1002', '小強': '1005','小張': '1006'}
>>> print('字典元素個數為:%d 個' % len(student))
字典元素個數為:4 個
輸出結果得到字典元素的個數為4。
5. type 函數
type(variable)函數返回輸入的變量類型,如果輸入變量是字典就返回字典類型,例如:
>>> student={'小萌': '1001', '小智': '1002', '小強': '1005','小張': '1006'}
>>> print('字典的類型為:',type(student))
字典的類型為: <class 'dict'>
輸出結果為字典(dict)類型。
3 字典的格式化字符串
在第4章我們已經見過如何使用字符串格式化功能格式化元組中的所有值。如果使用字典(只以字符串作為鍵)而不是元組做這項工作,會使字符串格式化更有趣一些,例如:
>>> student={'小萌':'1001','小智':'1002','小強':'1003'}
>>> print('小強的學號是:%(小強)s' % student)
小強的學號是:1003
由操作結果看到,字典的格式化方式是在每個轉換說明符中的%字符后加上用圓括號括起來的鍵,再跟上其他說明元素。
字典的格式化除了增加字符串鍵外,轉換說明符還像以前一樣工作。以這種方式使用字典時,只要所有給出的鍵都能在字典中找到,就可以獲得任意數量的轉換說明符。
4 字典和列表的區別
例如,以一個名字查找對應的學號。
如果用list實現,就要先在名字列表中找到對應的名字,再從學號列表取出對應的學號,list越長耗時越長。如果用dict實現,只需要一個名字和學號的對照表,就可以直接根據名字查找學號,無論這個表有多大,查找速度都不會變慢。
為什么dict查找速度這么快?
因為dict的實現原理和查字典一樣。假設字典包含10000個漢字,我們要查某一個字,一種方法是把字典從第一頁往后翻,直到找到我們想要的字為止,這種方法是在list中查找元素,list越大查找越慢。另一種方法是在字典的索引表里(如部首表)查這個字對應的頁碼,然后直接翻到該頁找到這個字。無論找哪個字,這種查找速度都非常快,不會隨著字典大小的增加而變慢。
dict就是第二種實現方法,給定一個名字,比如我們要查找5.2.3小節示例中“小萌”的學號,在dict內部就可以直接計算出“小萌”存放學號的“頁碼”,也就是1001存放的內存地址,直接取出來即可,所以速度非常快。
綜上所述,list和dict各有以下幾個特點:
dict的特點是:
(1)查找和插入的速度極快,不會隨著key的增加而變慢。
(2)需要占用大量內存,內存浪費多。
list的特點是:
(1)查找和插入時間隨著元素的增加而增加。
(2)占用空間小,浪費內存很少。
所以,dict是使用空間換取時間。
dict可以用在很多需要高速查找的地方,在Python代碼中幾乎無處不在,正確使用dict非常重要,需要牢記dict的鍵必須是不可變對象。
注意:dict內部存放的順序和鍵放入的順序沒有關系。
字典方法
像其他內建類型一樣,字典也有方法,這些方法非常有用,不過可能不像使用列表或字符串一樣頻繁使用。學習本節時可以先簡單瀏覽了解一下有哪些方法可以使用,然后在需要時回過頭來查看特定方法的具體用法。
1 clear()方法
clear()方法用于刪除字典內的所有項。
clear()方法的語法如下:
dict.clear()
此語法中dict代表指定字典,該方法不需要參數。該函數是一個原地操作(類似于list.sort)函數,沒有任何返回值(返回值為None)。
該方法使用示例如下:
>>> student={'小萌': '1001', '小智': '1002', '小強': '1005','小張': '1006'}
>>> print('字典元素個數為:%d 個' % len(student))
字典元素個數為:4 個
>>> student.clear()
>>> print('字典刪除后元素個數為:%d 個' % len(student))
字典刪除后元素個數為:0 個
由輸出結果可知,字典調用clear方法后整個字典內所有項都被刪除。
下面看兩個示例。
示例1:
>>> x={}
>>> y=x
>>> x['key']='value'
>>> y
{'key': 'value'}
>>> x={}
>>> y
{'key': 'value'}
示例2:
>>> x={}
>>> y=x
>>> x['key']='value'
>>> y
{'key': 'value'}
>>> x.clear()
>>> y
{}
兩個示例中,x和y最初對應同一個字典。示例1中,通過將x關聯到一個新的空字典對它重新賦值,這對y沒有任何影響,還關聯到原先的字典。若想清空原始字典中所有元素,則必須使用clear方法,使用clear后,y的值也被清空了。
2 copy()方法
copy()方法返回一個具有相同鍵/值對的新字典。這個方法是淺復制(shallow copy),因為值本身是相同的,而不是副本。
copy()方法的語法如下:
dict.copy()
此語法中dict代表指定字典,該方法不需要參數。返回結果為一個字典的淺復制。該方法使用示例如下:
>>> student={'小萌': '1001', '小智': '1002', '小強': '1005','小張': '1006'}
>>> st=student.copy()
>>> print('復制后得到的st為:',st)
復制后得到的st為: {'小強': '1005', '小萌': '1001', '小智': '1002', '小張': '1006'}
由輸出結果看到,使用copy方法可以將字典復制給另一個變量。
我們可以通過下面的示例了解什么是淺復制。
>>> student={'小智': '1002', 'info':['小張','1006','man']}
>>> st=student.copy()
>>> st['小智']='1005'
>>> print('更改copy 后的st為:',st)
更改copy 后的st為: {'info': ['小張', '1006', 'man'], '小智': '1005'}
>>> print('原字符串為::',student)
原字符串為:: {'info': ['小張', '1006', 'man'], '小智': '1002'}
>>> st['info'].remove('man')
>>> print('刪除后st為:',st)
刪除后st為: {'info': ['小張', '1006'], '小智': '1005'}
>>> print('刪除后student為:',student)
刪除后student 為: {'info': ['小張', '1006'], '小智': '1002'}
由輸出結果看到,替換副本的值時原始字典不受影響。如果修改了某個值(原地修改,不是替換),原始字典就會改變,因為同樣的值也在原字典中。以這種方式進行復制就是淺復制,而使用深復制(deep copy)可以避免該問題,此處不做講解,有興趣的讀者可以自己查找相關資料。
3 fromkeys()方法
fromkeys()方法用于創建一個新字典,以序列seq中的元素做字典的鍵,value為字典所有鍵對應的初始值。
fromkeys()方法的語法如下:
dict.fromkeys(seq[, value]))
此語法中dict代表指定字典;seq代表字典鍵值列表;value代表可選參數,設置鍵序列(seq)的值。該方法返回結果為列表。
該方法使用示例如下:
>>> seq = ('name', 'age', 'sex')
>>> info = dict.fromkeys(seq)
>>> print ("新的字典為 : %s" % info)
新的字典為 : {'name': None, 'sex': None, 'age': None}
>>> info = dict.fromkeys(seq, 10)
>>> print ("新的字典為 : %s" % info)
新的字典為 : {'name': 10, 'sex': 10, 'age': 10}
由輸出結果看出,fromkeys方法使用給定的鍵建立新字典,每個鍵默認對應的值為None。
4 get()方法
get()方法返回指定鍵的值,如果值不在字典中,就返回默認值。
get()方法的語法如下:
dict.get(key, default=None)
此語法中dict代表指定字典,key代表字典中要查找的鍵,default代表指定鍵的值不存在時返回默認值。該方法返回結果為指定鍵的值,如果值不在字典中,就返回默認值None。
該方法使用示例如下:
>>> student={'小萌': '1001', '小智': '1002'}
>>> print ('小萌的學號為: %s' % student.get('小萌'))
小萌的學號為: 1001
由輸出結果看到,get方法使用起來比較簡單。再看如下示例:
>>> st={}
>>> print(st['name'])
Traceback (most recent call last):
File "<pyshell#166>", line 1, in <module>
print(st['name'])
KeyError: 'name'
>>> print(st.get('name'))
None
>>> print('name 的值為:%s' % st.get('name'))
name 的值為:None
由輸出結果看到,其他方法試圖訪問字典中不存在的項時會出錯,而使用get方法就不會。使用get方法訪問一個不存在的鍵時,返回None。這里可以自定義默認值,用于替換None,例如:
>>> st={}
>>> print('name 的值為:%s' % st.get('name','未指定'))
name 的值為:未指定
由輸出結果看到,輸出結果中用“未指定”替代了None。
5 key in dict方法
Python字典in操作符用于判斷鍵是否存在于字典中,如果鍵在字典dict中就返回true,否則返回false。
該方法的語法如下:
key in dict
此語法中dict代表指定字典,key代表要在字典中查找的鍵。如果鍵在字典里就返回true,否則返回false。
該方法使用示例如下:
>>> student={'小萌': '1001', '小智': '1002'}
>>> print('小萌在student字典中:%s'%('小萌' in student))
小萌在student 字典中:True
>>> print('小強在student字典中:%s'%('小強' in student))
小強在student 字典中:False
由輸出結果看到,已返回對應的True或False。
該方法是Python 3中的方法。在Python 2中有一個有相同功能的方法——has_key方法,has_key方法的使用方式和in不同。
6 items()方法
items()方法以列表返回可遍歷的(鍵,值)元組數組。
items()方法的語法如下:
dict.items()
此語法中dict代表指定字典,該方法不需要參數。返回結果為可遍歷的(鍵/值)元組數組。
該方法使用示例如下:
>>> student={'小萌': '1001', '小智': '1002'}
>>> print('調用items方法的結果:%s'% student.items())
調用items 方法的結果:dict_items([('小萌', '1001'), ('小智', '1002')])
由輸出結果看到,返回結果為一個元組數組。
在Python 2中提供了一個iteritems方法,該方法和items方法的作用大致相同,但是iteritems方法返回的是一個迭代器對象,而不是列表。在Python 3中沒有iteritems方法。
7 keys()方法
keys()方法以列表返回一個字典所有鍵。
keys()方法的語法如下:
dict.keys()
此語法中dict代表指定字典,該方法不需要參數。返回結果為一個字典所有鍵。
該方法使用示例如下:
>>> student={'小萌': '1001', '小智': '1002'}
>>> print('字典student所有鍵為:%s'% student.keys())
字典student 所有鍵為:dict_keys(['小萌', '小智'])
由輸出結果看到,返回的是一個元組數組,數組中包含字典所有鍵。
8 setdefault()方法
setdefault()方法和get()方法類似,用于獲得與給定鍵相關聯的值。如果鍵不存在于字典中,就會添加鍵并將值設為默認值。
setdefault()方法的語法如下:
dict.setdefault(key, default=None)
此語法中dict代表指定字典,key代表查找的鍵值,default代表鍵不存在時設置的默認鍵值。該方法沒有任何返回值。
該方法使用示例如下:
>>> student={'小萌': '1001', '小智': '1002'}
>>> print('小強的鍵值為:%s'% student.setdefault('小強'))
小強的鍵值為:None
>>> print('小智的鍵值為:%s'% student.setdefault('小智'))
小智的鍵值為:1002
>>> print('student 字典新值為:%s'% student)
student 字典新值為:{'小強': None, '小萌': '1001', '小智': '1002'}
由輸出結果看到,當鍵不存在時,setdefault方法返回默認值并更新字典;如果鍵存在,就返回與其對應的值,不改變字典。和get一樣,默認值可以選擇,如果不設定就使用None,如果設定就使用設定的值,例如:
>>> student={'小萌': '1001', '小智': '1002'}
>>> print('小強的鍵值為:%s'% student.setdefault('小強'))
小強的鍵值為:None
>>> print('小zhang 的鍵值為:%s'% student.setdefault('小zhang','1006'))
小zhang 的鍵值為:1006
由輸出結果看到,小強沒有設置值,使用的是默認值,輸出鍵值為None;小zhang設置的默認值是1006,輸出鍵值為1006。
9 update()方法
update()方法用于把字典dict2的鍵/值對更新到dict里。
update()方法的語法如下:
dict.update(dict2)
此語法中dict代表指定字典,dict2代表添加到指定字典dict里的字典。該方法沒有任何返回值。
該方法使用示例如下:
>>> student={'小萌': '1001', '小智': '1002'}
>>> student2={'小李':'1003'}
>>> print('原student 字典為:%s'% student)
原student字典為:{'小萌': '1001', '小智': '1002'}
>>> student.update(student2)
>>> print('新student 字典為:%s'% student)
新student字典為:{'小李': '1003', '小萌': '1001', '小智': '1002'}
>>> student3={'小李':'1005'}
>>> student.update(student3) #對相同項覆蓋
>>> print('新student 字典為:%s'% student)
新student字典為:{'小李': '1005', '小萌': '1001', '小智': '1002'}
由輸出結果看到,提供的字典中的項被添加到舊字典中,如果有相同的鍵就會覆蓋。
10 values()方法
values()方法以列表形式返回字典中所有值。與返回鍵的列表不同,返回的列表中可以包含重復的元素。
values()方法的語法如下:
dict.values()
此語法中dict代表指定字典,該方法不需要參數。返回結果為字典中的所有值。
該方法使用示例如下:
>>> student={'小萌': '1001', '小智': '1002','小李':'1001'}
>>> print('student 字典所有值為:%s'% list(student.values()))
student 字典所有值為:['1001', '1001', '1002']
由輸出結果看到,返回的列表中包含重復的元素。
調試
下面我們通過示例進行介紹,這里通過設置一些錯誤讓讀者認識在編寫代碼過程中的常見問題,以幫助讀者熟悉和解決實際遇到的問題。
(1)使用列表根據姓名查找學號,學號使用字符串表示,如果更改為使用數字表示會如何?例如:
>>> students=['小萌','小智','小強','小張','小李']
>>> numbers=[1001,1002,1003,1004,1005]
>>> print('小智的學號是:',numbers[students.index('小智')])
小智的學號是: 1002
輸出結果和使用字符串表示的輸出結果沒有什么不同。不過這里數字都是以1開頭,若把1更改為0,我們試試:
>>> students=['小萌','小智','小強','小張','小李']
>>> numbers=[0001,0002,0003,0004,0005]
SyntaxError: invalid token
可以看出,numbers的編譯不讓通過,告訴我們這是一個無效標記。這就是不使用數字而使用字符串的原因,使用數字碰到以0開頭的數字就會出現問題。
(2)嘗試從字典中輸出一個字符寬度為10的元素?例如:
>>> student={'小萌':'1001','小智':'1002','小強':'1003'}
>>> print('小萌的學號是:%(小萌)10s' % student) #字符寬度為10
小萌的學號是: 1001
嘗試把10s變換為10d、10f、-10s、+10s,看看輸出結果是怎樣的。