《Python從小白到大牛》第9章 數據結構

《Python從小白到大牛》已經上市!
《Python從小白到大牛》第9章 數據結構
當你有很多書時,你會考慮買一個書櫃,將你的書分門別類擺放進入。使用了書櫃不僅僅使房間變得整潔,也便於以後使用書時方便查找。在計算機程序中會有很多數據,這些數據也需要一個容器將他們管理起來,這就是數據結構。常見的數據結構:數組(Array)、集合(Set)、列表(List)、隊列(Queue)、鏈表(Linkedlist)、樹(Tree)、堆(Heap)、棧(Stack)和字典(Dictionary)等結構。

Python中數據容器主要有:序列、集合和字典。

注意
Python中並沒有數組結構,因爲數組要求元素類型是一致的。而Python作爲動態類型語言,不強制聲明變量的數據類型,也不能強制檢查元素的數據類型。所以Python中沒有數組結構。

元組

元組(tuple)是一種序列(sequence)結構,下面先來介紹一些序列。

序列

序列(sequence)是一種可迭代的^1,元素是有序的,可以重複出現的數據結構。序列可以通過索引訪問元素。圖9-1是一個班級序列,其中有一些學生,這些學生是有序的,順序是他們被放到序列中的順序,可以通過序號訪問他們。這就像老師給進入班級的人分配學號,第一個報到的是“張三”,老師給他分配的是0,第二個報到的是“李四”,老師給他分配的是1,以此類推,最後一個序號應該是“學生人數-1”。

圖9-1 序列

序列包括的結構有:列表(list)、字符串(str)、元組(tuple)、範圍(range)、和字節序列(bytes)。序列可進行的操作有:索引、分片、加和乘。

  1. 索引操作

序列中第一個元素的索引是0,其他元素的索引是第一個元素的偏移量。可以有正偏移量,稱爲正值索引;也可以有負偏移量,稱爲負值索引。正值索引最後一個元素索引是“序列長度-1”,負值索引最後一個元素索引是“-1”。例如Hello字符串,它的正值索引如圖9-2(a)所示,它的負值索引如圖9-2(b)所示。

圖9‑2 索引

訪問序列中的元素的通過索引下標訪問的,即中括號[index]方式訪問。在Python
Shell中運行示例如下:

>>> a = 'Hello'
>>> a[0]
'H'
>>> a[1]
'e'
>>> a[4]
'o'
>>> a[-1]
'o'
>>> a[-2]
'l'
>>> a[5] 
Traceback (most recent call last):
  File "<pyshell#2>", line 1, in <module>
    a[5]
IndexError: string index out of range
>>> max(a)
'o'
>>> min(a)
'H'
>>> len(a)
5

a[0]是訪問序列第一個元素,最後一個元素的索引可以是4或-1。但是索引超過範圍,則會發生IndexError錯誤。另外,獲取序列的長度使用函數len,類似的序列還有max和min函數,max函數返回最後一個元素,min函數返回第一個元素。  

  1. 序列的加和乘

下面看看序列的加和乘,在前面第7章介紹+和*運算符時,提到過他們可以應用於序列。+運算符可以將兩個序列連接起來,*運算符可以將重複多次。

在Python Shell中運行示例:

>>> a = 'Hello'
>>> a * 3
'HelloHelloHello'
>>> print(a)
Hello
>>> a += ' '
>>> a += 'World'
>>> print(a)
Hello World
  1. 序列分片

序列的分片(Slicing)就是從序列中切分出小的子序列。分片使用分片運算符,分片運算符有兩種形式:

  • [start:end]。start是開始索引,end是結束索引。

  • [start:end:step]。start是開始索引,end是結束索引,step是步長,步長是在分片時獲取元素的間隔。步長可以爲正整數,也可爲負整數。

注意
切下的分片包括start位置元素,但不包括end位置元素,start和end都可以省略。

在Python Shell中運行示例代碼如下:

>>> a[1:3]
'el'
>>> a[:3]   
'Hel'
>>> a[0:3]
'Hel'
>>> a[0:]   
'Hello'
>>> a[0:5]
'Hello'
>>> a[:]    
'Hello'
>>> a[1:-1] 
'ell'

上述代碼表達式a[1:3]是切出1\~3之間的子字符串,注意不包括3,所以結果是el。表達式a[:3]省略了開始索引,默認開始索引是0,所以a[:3]與a[0:3]分片結果是一樣的。表達式a[0:]省略了結束索引,默認結束索引是序列的長度,即5。所以a[0:]
與a[0:5]分片結果是一樣的。表達式a[:]是省略了開始索引和結束索引,a[:]與a[0:5]結果一樣。

另外,表達式a[1:-1]使用了負值索引,對照圖9-1所示,不難計算出a[1:-1]結果是ell。

分片時使用[start:end:step]可以指定步長(step),步長與當次元素索引、下次元素索引之間的關係如下:

>   下次元素索引 = 當次元素索引 + 步長

在Python Shell中運行示例代碼如下:

>>> a[1:5]
'ello'
>>> a[1:5:2]
'el'
>>> a[0:3]
'Hel'
>>> a[0:3:2]
'Hl'
>>> a[0:3:3]
'H'
>>> a[::-1]
'olleH'

表達式a[1:5]省略了步長參數,步長默認值是1。表達式a[1:5:2]是步長爲2,結果是el。a[0:3]分片後的字符串是Hel。而a[0:3:3]是步長爲3,分片結果H字符了。當步長爲負數時比較麻煩,負數時是從右往左獲取元素,所以表達式a[::-1]分片的結果是原始字符串的倒置。

創建元組

元組(tuple)是一種不可變序列,一旦創建就不能修改。創建元組可以使用tuple([iterable])函數或者直接用逗號(,)將元素分隔。

在Python Shell中運行示例代碼如下:

>>> 21,32,43,45             ①
(21, 32, 43, 45)
>>> (21, 32, 43, 45)            ②
(21, 32, 43, 45)
>>> a = (21,32,43,45)
>>> print(a)
(21, 32, 43, 45)
>>> ('Hello', 'World')      ③
 ('Hello', 'World')
>>> ('Hello', 'World', 1,2,3)④
('Hello', 'World', 1, 2, 3)
>>> tuple([21,32,43,45])      ⑤
(21, 32, 43, 45)

代碼第①行創建了一個有4個元素的元組,創建元組時使用小括號把元素包裹起來不是必須的。代碼第②行使用括號將元素包裹起來,這只是爲了提高程序的可讀性。Python中沒有強制聲明數據類型,因此元組中的元素可以是任何數據類型,代碼第③行創建是一個字符串元組,代碼第④行是創建字符串和整數混合的元組。

另外,元組還有通過tuple([iterable])函數創建,參數iterable是任何可迭代對象。代碼第⑤行是使用tuple()函數創建元組對象,實參[21,32,43,45]是一個列表,列表是可迭代對象,可以作爲tuple()函數參數創建元組對象。

創建元組還需要注意如下極端情況:

>>> a = (21)
>>> type(a)
<class 'int'>
>>> a = (21,)
>>> type(a)
<class 'tuple'>
>>> a = ()  
>>> type(a)
<class 'tuple'>

從上述代碼可見,如果一個元組只有一個元素時,後面的逗號不能省略,即(21,)表示的是隻有一個元素的元組,而(21)表示的是一個整數。另外,()可以創建空元組。

訪問元組

元組做爲序列可以通過下標索引訪問元素,也可以對其進行分片。在Python
Shell中運行示例代碼如下:

>>> a =  ('Hello', 'World', 1,2,3)  ①
>>> a[1]
'World'
>>> a[1:3]
('World', 1)
>>> a[2:]
(1, 2, 3)
>>> a[:2]
('Hello', 'World')

上述代碼第①行是元組a,a[1]是訪問元組第二個元素,表達式a[1:3]、a[2:]和a[:2]都是進行分片操作。

元組還可以進行拆包(Unpack)操作,就是將元組的元素取出賦值給不同變量。在Python Shell中運行示例代碼如下:

>>> a =  ('Hello', 'World', 1,2,3)
>>> str1, str2, n1,n2, n3 = a   ①
>>> str1
'Hello'
>>> str2
'World'
>>> n1
1
>>> n2
2
>>> n3
3
>>> str1, str2, *n = a  ②
>>> str1
'Hello'
>>> str2
'World'
>>> n
[1, 2, 3]
>>> str1,_,n1,n2,_ = a ③

上述代碼第①行是將元組a進行拆包操作,接收拆包元素的變量個數應該等於元組個數相同。接收變量個數也可以少於元組個數,代碼第②行接收變量個數只有3個,最後一個很特殊,變量n前面有星號,表示將剩下的元素作爲一個列表賦值給變量n。另外,還可以使用下劃線指定哪些元素不取值,代碼第行是不取第二個和第五個元素。

遍歷元組

遍歷元組一般是使用for循環,示例代碼如下:

# coding=utf-8
# 代碼文件:chapter9/ch9.1.4.py

a = (21, 32, 43, 45)

for item in a:              ①
    print(item)

print('-----------')
for i, item in enumerate(a):    ②
    print('{0} - {1}'.format(i, item))

輸出結果如下:

21
32
43
45
-----------
0 - 21
1 - 32
2 - 43
3 – 45

一般情況下遍歷目的只是取出每一個元素值,見代碼第①行的for循環。但有時需要在遍歷過程中同時獲取索引,則可以使用代碼第②行的for循環,其中enumerate(a)函數可以獲得元組對象,該元組對象有兩個元素,第一個元素是索引,第二個元素是數值。所以i,
item是元組拆包過程,最後變量i是元組a的當前索引,item是元組a的當前元素值。

注意
本節雖然介紹的是元組的遍歷,上述遍歷方式適合於所有序列,如字符串、範圍和列表等。

列表

列表(list)也是一種序列結構,與元組不同列表具有可變性,可以追加、插入、刪除和替換列表中的元素。

列表創建

創建列表可以使用list([iterable])函數,或者用中括號[]將元素包裹,元素之間用逗號分隔。在Python Shell中運行示例代碼如下:

>>> [20, 10, 50, 40, 30]    ①
[20, 10, 50, 40, 30]
>>> []  
[]
>>> ['Hello', 'World', 1, 2, 3] ②
['Hello', 'World', 1, 2, 3]
>>> a = [10]        ③
>>> type(a)
<class 'list'>
>>> a = [10,]       ④
>>> type(a)
<class 'list'>
>>> list((20, 10, 50, 40, 30))  ⑤
[20, 10, 50, 40, 30]

上述代碼第①行創建一個有5個元素的列表,注意中括號不能省略,如果省略了中括號那就變成了元組了。創建空列表是[]表達式。列表中可以放入任何對象,代碼第②行是創建一個字符串和整數混合的列表。代碼第③行是創建只有一個元素的列表,中括號不能省略。另外,無論是元組還是列表,每一個元素後面都跟着一個逗號,只是最後一個元素的逗號經常是省略的,代碼第④行最後一個元素沒有省略逗號。

另外,列表還有通過list([iterable])函數創建,參數iterable是任何可迭代對象。代碼第⑤行是使用list()函數創建列表對象,實參(20,
10, 50, 40,
30)是一個元組,元組是可迭代對象,可以作爲list()函數參數創建列表對象。

追加元素

列表中追加單個元素可以使用append()方法追加單個元素。如果想追加另一列表,可以使用+運算符或extend()方法。

append()方法語法:

list.append(x)

其中x參數是要追加單個元素值。

extend()方法語法:

list.extend(t)

其中t參數是要追加的另外一個列表。

在Python Shell中運行示例代碼如下:

>>> student_list = ['張三', '李四', '王五']   
>>> student_list.append('董六')       ①
>>> student_list
['張三', '李四', '王五', '董六']
>>> student_list += ['劉備', '關羽']        ②
>>> student_list
['張三', '李四', '王五', '董六', '劉備', '關羽']
>>> student_list.extend(['張飛', '趙雲'])   ③
>>> student_list
['張三', '李四', '王五', '董六', '劉備', '關羽', '張飛', '趙雲']

上述代碼中第①行使用了append方法,在列表後面追加一個元素,append()方法不能同時追加多個元素。代碼第②行是利用+=運算符追加多個元素,能夠支持+=運算是因爲列表支持+運算。代碼第③行是使用extend()方法追加多個元素。

插入元素

插入元素可以使用列表的insert()方法,該方法可以在指定索引位置,插入一個元素。insert()方法語法:

list.insert(i, x)

其中參數i是要插入的索引,參數x是要插入的元素數值。

在Python Shell中運行示例代碼如下:

>>> student_list = ['張三', '李四', '王五']
>>> student_list.insert(2, '劉備')
>>> student_list
['張三', '李四', '劉備', '王五'] 

上述代碼中student_list調用insert方法,在索引2位置插入一個元素,新元素的索引爲2。

替換元素

列表具有可變性,其中的元素替換,替換元素很簡單,通過列表下標索引元素放在賦值符號(=)左邊,進行賦值即可替換。在Python
Shell中運行示例代碼如下:

>>> student_list = ['張三', '李四', '王五']
>>> student_list[0] = "諸葛亮"
>>> student_list
['諸葛亮', '李四', '劉備', '王五']

其中student_list[0] = "諸葛亮"是替換列表student_list的第一個元素。

刪除元素

列表中實現刪除元素的方式有兩種:一種是使用列表的remove()方法;另一種是使用列表的pop()方法。

  1. remove()方法

remove()方法從左往右查找列表中的元素,如果找到匹配元素則刪除,注意如果找到多個匹配元素,只是刪除第一個。如果沒有找到則會拋出錯誤。

remove()方法語法:

list.remove(x)

其中x參數是要找到元素值。

使用remove()方法刪除元素,示例代碼如下:

>>> student_list = ['張三', '李四', '王五', '王五']
>> student_list.remove('王五')
>>> student_list
['張三', '李四', '王五']
>>> student_list.remove('王五')
>>> student_list
['張三', '李四']
  1. pop()方法

pop()方法也會刪除列表中的元素,但它會將成功刪除的元素返回。pop()方法語法如下:

item = list.pop([i])

參數i是指定刪除元素的索引,i可以省略,表示刪除最後一個元素。返回值item是刪除的元素。

使用pop()方法刪除元素示例代碼如下:

>>> student_list = ['張三', '李四', '王五']
>>> student_list.pop()
'王五'
>>> student_list
['張三', '李四']
>>> student_list.pop(0)
'張三'
>>> student_list
['李四']

其他常用方法

前面介紹列表追加、插入和刪除時,已經介紹了一些方法。事實上列表還有很多方法,本節再介紹幾個常用的方法。包括:

  • reverse()。倒置列表。

  • copy()。複製列表。

  • clear()。清除列表中的所有元素。

  • index(x[, i[,
    j]])。返回查找x第一次出現的索引,i是開始查找索引,j是結束查找索引。該方法繼承自序列,元組和字符串也可以使用該方法。

  • count(x)。返回x出現的次數。該方法繼承自序列,元組和字符串也可以使用該方法。

在Python Shell中運行示例代碼如下:

>>> a = [21, 32, 43, 45]
>>> a.reverse()     ①
>>> a
[45, 43, 32, 21]
>>> b = a.copy()    ②
>>> b
[45, 43, 32, 21]
>>> a.clear()           ③   
>>> a
[]
>>> b
[45, 43, 32, 21]
>>> a = [45, 43, 32, 21, 32]
>>> a.count(32)     ④
2
>>> student_list = ['張三', '李四', '王五']
>>> student_list.index('王五')    ⑤
2
>>> student_tuple = ('張三', '李四', '王五')
>>> student_tuple.index('王五')   ⑥
2
>>> student_tuple.index('李四', 1 , 2)
1

上述代碼中第①行是調用reverse()方法將列表a倒置。代碼第②行是調用copy()方法複製a,並賦值給b。代碼第③行是清除a中元素。代碼第④行是返回a列表中32元素的個數。代碼第⑤行是返回'王五'在student_list列表中的位置。代碼第⑥行是返回'王五'在student_tuple元組中的位置。

列表推導式

Python中有一種特殊表達式——推導式,它可以將一種數據結構作爲輸入,經過過濾、計算等處理,最後輸出另一種數據結構。根據數據結構的不同分爲:列表推導式、集合推導式和字典推導式。本節先介紹列表推導式。

如果想獲得0\~9中偶數的平方數列,那麼可以通過for循環實現,代碼如下:

# coding=utf-8
# 代碼文件:chapter9/ch9.2.7.py

n_list = []
for x in range(10):
    if x % 2 == 0:
        n_list.append(x ** 2)
print(n_list)

輸出結構如下:

[0, 4, 16, 36, 64]

0\~9中偶數的平方數列可以通過列表推導式實現,代碼如下:

n_list = [x ** 2 for x in range(10) if x % 2 == 0]  ①
print(n_list)

上述代碼其中代碼第行就是列表推導式,輸出的結果與for循環是一樣的。圖9-3所示是列表推導式語法結構,其中in後面的表達式是“輸入序列”;for前面的表達式是“輸出表達式”它運算結果會保存一個新列表中;if條件語句是過濾輸入序列,符合條件的才傳遞給輸出表達式,“條件語句”是可以省略的,也是所有元素都傳遞給輸出表達式。

圖9‑3 索引

條件語句可以包含多個條件,如果想找出0\~99之間的偶數,而且可以被5整除數列,實現代碼如下:

n_list = [x for x in range(100) if x % 2 == 0 if x % 5 == 0]  
print(n_list)

列表推導式的條件語句有兩個if x % 2 == 0和if x % 5 == 0,可見他們“與”的關係。

集合

集合(set)是一種可迭代的、無序的、不能包含重複元素的數據結構。圖9-4是一個班級的集合,其中包含一些學生,這些學生是無序的,不能通過序號訪問,而且不能有重複的同學。

圖9-4 集合

提示
如果與序列比較,序列中的元素是有序的,可以重複出現,而集合中是無序的,不能重複的元素。序列強調的是有序,集合強調的是不重複。當不考慮順序,而且沒有重複的元素時,序列和集合可以互相替換。

集合又分爲可變集合(set)和不可變集合(frozenset)。

創建可變集合

可變集合類型是set,創建可變集合可以使用set([iterable])函數,或者用大括號{}將元素包裹,元素之間用逗號分隔。在Python
Shell中運行示例代碼如下:

>>> a = {'張三', '李四', '王五'}      ①
>>> a
{'張三', '李四', '王五'}
>>> a = {'張三', '李四', '王五', '王五'}②
>>> len(a)
3
>>> a
{'張三', '李四', '王五'}
>>> set((20, 10, 50, 40, 30)) ③
{40, 10, 50, 20, 30}
>>> b = {}          ④
>>> type(b)
<class 'dict'>
>>> b = set()       ⑤
>>> type(b)
<class 'set'>

上述代碼第①行是使用大括號創建集合,如果元素有重複的會怎樣呢?代碼第②行包含有重複的元素,創建時會剔除重複元素。代碼第③行是使用set()函數創建集合對象。如果要創建一個空的集合不能使用{}表示,見代碼第④行b並不是集合而是字典,而是使用空參數的set()函數,見代碼第⑤行。

提示
要獲得集合中元素的個數,可以使用len()函數,注意len()是函數不是方法,本例中len(a)表達式返回集合a的元素個數。

修改可變集合

可變集合類似於列表,可變集合內容可以被修改,可以插入和刪除元素。修改可變集合幾個常用的方法。包括:

  • add(elem)。添加元素,如果元素已經存在,則不能添加,不會拋出錯誤。

  • remove(elem)。刪除元素,如果元素不存在,則拋出錯誤。

  • discard(elem)。刪除元素,如果元素不存在,不會拋出錯誤。

  • pop()。刪除返回集合中任意一個元素,返回值是刪除的元素。

  • clear()。清除集合。

在Python Shell中運行示例代碼如下:

>>> student_set = {'張三', '李四', '王五'}
>>> student_set.add('董六')
>>> student_set
{'張三', '董六', '李四', '王五'}
>>> student_set.remove('李四')
>>> student_set
{'張三', '董六', '王五'}
>>> student_set.remove('李四')    ①
Traceback (most recent call last):
  File "<pyshell#144>", line 1, in <module>
    student_set.remove('李四')
KeyError: '李四'
>>> student_set.discard('李四')   ②
>>> student_set
{'張三', '董六', '王五'}
>>> student_set.discard('王五')
>>> student_set
{'張三', '董六'}
>>> student_set.pop()       
'張三'
>>> student_set
{'董六'}
>>> student_set.clear()
>>> student_set
set()

上述代碼第①行使用remove()方法刪除元素時,由於要刪除的'李四'已經不在集合中,所以會拋出錯誤。而同樣是刪除集合中不存在的元素discard()方法不會拋出錯誤,見代碼第②行。

遍歷集合

集合是無序的,沒有索引,不能通過下標訪問單個元素。但可以遍歷集合,訪問集合每一個元素。

遍歷集合一般是使用for循環,示例代碼如下:

# coding=utf-8
# 代碼文件:chapter9/ch9.3.3.py

student_set = {'張三', '李四', '王五'}

for item in student_set:     
    print(item)

print('-----------')
for i, item in enumerate(student_set):    ①
    print('{0} - {1}'.format(i, item))

輸出結果如下:

張三
王五
李四
-----------
0 - 張三
1 - 王五
2 - 李四

上述中使用for循環遍歷集合,代碼第①行的for循環中使用了enumerate()函數,該還是在9.1.4節遍歷元組時已經介紹過了,但是需要注意的是,此時變量i不是索引,只是遍歷集合的次數。

不可變集合

不可變集合類型是frozenset,創建不可變集合使用frozenset([iterable])函數,不能使用大括號{}。在Python Shell中運行示例代碼如下:

>>> student_set = frozenset({'張三', '李四', '王五'})  ①  
>>> student_set
frozenset({'張三', '李四', '王五'})
>>> type(student_set)
<class 'frozenset'>
>>> student_set.add('董六')   ②
Traceback (most recent call last):
  File "<pyshell#168>", line 1, in <module>
    student_set.add('董六')
AttributeError: 'frozenset' object has no attribute 'add'
>>> a = (21, 32, 43, 45)
>>> seta = frozenset(a) ③
>>> seta
frozenset({32, 45, 43, 21})

上述代碼第①行是創建不可變集合,frozenset()的參數{'張三', '李四',
'王五'}是另一個集合對象,因爲集合也是可迭代對象,可以作爲frozenset()的參數。代碼第③函數使用的了一個元組a作爲frozenset()的參數。

由於創建的是不變集合,不能被修改,所以視圖修改發生錯誤,見代碼第②行,使用add()發生錯誤。

集合推導式

集合推導式與列表推斷式類似,區別只是輸出結構是集合。修改9.2.7節代碼如下:

# coding=utf-8
# 代碼文件:chapter9/ch9.3.5.py

n_list = {x for x in range(100) if x % 2 == 0 if x % 5 == 0}
print(n_list)

輸出結構如下:

{0, 70, 40, 10, 80, 50, 20, 90, 60, 30}

由於集合是不能有重複元素的,集合推導式輸出結果會過濾掉重複的元素,示例代碼如下:

input_list = [2, 3, 2, 4, 5, 6, 6, 6]

n_list = [x ** 2 for x in input_list] ①
print(n_list)

n_set = {x ** 2 for x in input_list} ②
print(n_set)

輸出結構如下:

[4, 9, 4, 16, 25, 36, 36, 36]
{4, 36, 9, 16, 25}

上述代碼第①行是列表推導式。代碼第②行是集合推導式,從結果可見沒有重複的元素。

字典

字典(dict)是可迭代的、可變的數據結構,通過鍵來訪問元素的數據結構。字典結構比較複雜,它是由兩部分視圖構成的:一個是鍵(key)視圖;另一個是值(value)視圖。鍵視圖不能包含重複元素的,而值集合可以,鍵和值是成對出現的。

圖9-5所示是字典結構的“國家代號”。鍵是國家代號,值是國家。

圖9-5 字典

提示
字典更適合通過鍵快速訪問值,就像查英文字典一樣,鍵就是要查的英文單詞,而值是英文單詞的翻譯和解釋等內容。有的時候,一個英文單詞會對應多個翻譯和解釋,這也是與字典集合特性對應的。

創建字典

字典類型是dict,創建字典可以使用dict()函數,或者用大括號{}將“鍵:值”對包裹,“鍵:值”對之間用逗號分隔。

在Python Shell中運行示例代碼如下:

>>> dict1 = {102: '張三', 105: '李四', 109: '王五'} ①
>>> len(dict1)
3
>>> dict1
{102: '張三', 105: '李四', 109: '王五'}
>>> type(dict1)
<class 'dict'>
>>> dict1  = {}
>>> dict1
{}
>>> dict({102: '張三', 105: '李四', 109: '王五'})  ②
{102: '張三', 105: '李四', 109: '王五'}
>>> dict(((102, '張三'), (105, '李四'), (109, '王五'))) ③
{102: '張三', 105: '李四', 109: '王五'}
>>> dict([(102, '張三'), (105, '李四'), (109, '王五')]) ④
{102: '張三', 105: '李四', 109: '王五'}
>>> t1 = (102, '張三')
>>> t2 = (105, '李四')
>>> t3 = (109, '王五')
>>> t = (t1, t2, t3) 
>>> dict(t) ⑤
{102: '張三', 105: '李四', 109: '王五'}
>>> list1 = [t1, t2, t3] 
>>> dict(list1) ⑥
{102: '張三', 105: '李四', 109: '王五'}
>>> dict(zip([102, 105, 109], ['張三', '李四', '王五']))  ⑦
{102: '張三', 105: '李四', 109: '王五'}

上述代碼第①行是使用大括號“鍵:值”對創建字典,這是最簡單的創建字典方式了,那麼創建一個空字典表達式是{}。獲得字典長度(鍵值對個數)也是使用len()函數。

代碼第②行、第③行、第④行、第⑤行和第⑥行都用dict()函數創建字典。代碼第②行dict()函數參數是另外一個字典{102:
'張三', 105: '李四', 109: '王五'},使用這種方式不如直接使用大括號“鍵:值”。

代碼第③行和第⑤行參數是一個元組,這個元組中要包含三個只有兩個元素的元組,創建過程參考如圖9-6所示。代碼第④行和第⑥行參數是一個列表,這個列表中包含三個只有兩個元素的元組。

圖9-6 創建字典

代碼第⑦行是使用zip()函數,zip()函數將兩個可迭代對象打包成元組,在創建字典時,可迭代對象元組,需要兩個可迭代對象,第一個是鍵([102,
105, 109]),第二個是值(['張三', '李四',
'王五']),他們包含的元素個數相同,並且一一對應。

注意 使用dict()函數創建字典還可以使用一種key=value形式參數,語法如下:

dict(key1=value1, key2=value2, key3=value3...)

key=value形式只能創建鍵是字符串類型的字典,使用時需要省略包裹字符串的引號(包括雙引號或單引號)。在Python
Shell中運行示例代碼如下:

\>\>\> dict(102 = '張三', 105 = '李四', 109 = '王五') ①

SyntaxError: keyword can't be an expression

\>\>\> dict('102' = '張三', '105' = '李四', '109' = '王五') ②

SyntaxError: keyword can't be an expression

\>\>\> dict(S102 = '張三', S105 = '李四', S109 = '王五') ③

{'S102': '張三', 'S105': '李四', 'S109': '王五'}

代碼第①行試圖通過上述dict()函數創建鍵是整數類型的字典,結果會發生錯誤。代碼第②行是試圖使用字符串作爲鍵創建字典,但是該dict()函數需要省略字符串鍵的引號,因此會發生錯誤。需要注意本例中鍵是由數字構成的字符串,他們很特殊如果省略包裹他們的引號,那麼他們會表示爲數字,使用該dict()函數是不允許的,所以此時的鍵不會識別字符串類型。代碼第③行的鍵是在數字前面加S字母,這樣不會識別爲字符串類型。

修改字典

字典可以被修改,但都是針對鍵和值同時操作,修改字典包括添加、替換和刪除“鍵:值”對。

在Python Shell中運行示例代碼如下:

>>> dict1 = {102: '張三', 105: '李四', 109: '王五'} 
>>> dict1[109] ①
'王五'
>>> dict1[110] = '董六' ②
>>> dict1
{102: '張三', 105: '李四', 109: '王五', 110: '董六'}
>>> dict1[109] = '張三' ③
>>> dict1
{102: '張三', 105: '李四', 109: '張三', 110: '董六'}
>>> del dict1[109] ④
>>> dict1
{102: '張三', 105: '李四', 110: '董六'}
>>> dict1.pop(105)
'李四'
>>> dict1
{102: '張三', 110: '董六'}
>>> dict1.pop(105, '董六') ⑤
'董六'
>>> dict1.popitem() ⑥
(110, '董六')
>>> dict1
{102: '張三'}

訪問字典中元素可通過下標實現,下標參數是鍵,返回對應的值,代碼第①行是dict1[109]是取出字典dict1中鍵爲109的值。字典下標訪問元素也可以在賦值符號(=)左邊,代碼第②行是字典110鍵賦值,注意此時字典dict1中沒有110鍵,那麼這樣的操作會添加110:
'董六'鍵值對。如果鍵存在那麼會替換對應的值,代碼第③行會將鍵109對應的值替換爲'張三',雖然此時值視圖中已經有'張三'了,但仍然可以添加,這說明值是可以重複的。

代碼第④行是刪除109鍵對應的值,注意del是語句不是函數。使用del語句刪除鍵值對時,如果鍵不存在會拋出錯誤。

如果喜歡使用方法刪除元素,可以使用字典的pop(key[,
default])和popitem()方法。pop(key[,
default])方法刪除鍵值對,如果鍵不存在則返回默認值(default),見代碼第⑤行是105鍵不存在返回默認值'董六'。popitem()方法刪除任意鍵值對,返回刪除的鍵值對,構成的元組,上述代碼第⑥行刪除了一個鍵值對,返回一個元組對象(110,
'董六')。

訪問字典

字典還一些方法用來訪問它的鍵或值,這些方法如下:

  • get(key[, default])。通過鍵返回值,如果鍵不存在返回默認值。

  • items()。返回字典的所有鍵值對。

  • keys()。返回字典鍵視圖。

  • values()。返回字典值視圖。

在Python Shell中運行示例代碼如下:

>>> dict1 = {102: '張三', 105: '李四', 109: '王五'}
>>> dict1.get(105)  ①
'李四'
>>> dict1.get(101)  ②
>>> dict1.get(101, '董六') ③
'董六'
>>> dict1.items() 
dict_items([(102, '張三'), (105, '李四'), (109, '王五')])
>>> dict1.keys() 
dict_keys([102, 105, 109])
>>> dict1.values() 
dict_values(['張三', '李四', '王五'])

上述代碼第①行是通過get()方法返回105鍵對應的值,如果沒有鍵對應的值,而且還沒有爲get()方法提供默認值,則不會有返回值,見代碼第②行。代碼第③行是提供了返回值。

在訪問字典時,也可以使用in和not in運算符,但是需要注意的是in和not
in運算符只測試鍵視圖中進行。在Python Shell中運行示例代碼如下:

>>> student_dict = {'102': '張三', '105': '李四', '109': '王五'}
>>> 102 in dict1
True
>>> '李四' in dict1
False

遍歷字典

字典遍歷也是字典的重要操作。與集合不同,字典有兩個視圖,因此遍歷過程可以只遍歷值視圖,也可以只遍歷鍵視圖,也可以同時遍歷。這些遍歷過程都是通過for循環實現的。

示例代碼如下:

# coding=utf-8
# 代碼文件:chapter9/ch9.4.4.py

student_dict = {102: '張三', 105: '李四', 109: '王五'}  

print('---遍歷鍵---')
for student_id in student_dict.keys():  ①
    print('學號:' + str(student_id))

print('---遍歷值---')
for student_name in student_dict.values(): ②
    print('學生:' + student_name)

print('---遍歷鍵:值---')
for student_id, student_name in student_dict.items(): ③ 
    print('學號:{0} - 學生:{1}'.format(student_id, student_name))

輸出結果如下:

---遍歷鍵---
學號:102
學號:105
學號:109
---遍歷值---
學生:張三
學生:李四
學生:王五
---遍歷鍵:值---
學號:102 - 學生:張三
學號:105 - 學生:李四
學號:109 - 學生:王五

上述代碼第③行遍歷字典的鍵值對,items()方法返回是鍵值對元組序列,student_id,
student_name是從元組拆包出來的兩個變量。

字典推導式

因爲字典包含了鍵和值兩個不同的結構,因此字典推導式結果可以非常靈活。字典推導示例代碼如下:

# coding=utf-8
# 代碼文件:chapter9/ch9.4.5.py

input_dict = {'one': 1, 'two': 2, 'three': 3, 'four': 4} 

output_dict = {k: v for k, v in input_dict.items() if v % 2 == 0} ①
print(output_dict)

keys = [k for k, v in input_dict.items() if v % 2 == 0]  ②
print(keys)

輸出結構如下:

{'two': 2, 'four': 4}
['two', 'four']

上述代碼第①行是字典推導式,注意輸入結構不能直接使用字典,因爲字典不是序列,可以通過字典的item()方法返回字典中鍵值對序列。代碼第②行是字典推導式,但只返回鍵結構。

本章小結

本章介紹了Python中的幾種數據結構。其中包括序列、元組、集合和字典,瞭解序列的特點,清楚序列包括哪些結構。然後詳細介紹了元組、集合和字典。

配套視頻

http://edu.51cto.com/sd/f907b

配套源代碼

http://www.zhijieketang.com/group/8

作者微博:@tony_關東昇br/>郵箱:[email protected]
智捷課堂微信公共號:zhijieketang
Python讀者服務QQ羣:628808216

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