2020年10月29日星期四

012 與小卡特一起學 Python 第 12 章 收集起來——清單與字典

 申明本站飛宇網 https://feiyetopro.blogspot.com/自網路收集整理之書籍文章影音僅供預覽交流學習研究,其[書籍、文章、影音]情節內容, 評論屬其個人行為, 與本網站無關。版權歸原作者和出版社所有,請在下載 24 小時內刪除,不得用作商業用途;如果您喜歡其作品,請支持訂閱購買[正版]謝謝!


12 章 收集起來——清單與字典

我們已經見過 Python 可以在記憶體中存儲資訊,還可以用名字來獲取原先存儲的資訊。到目前為止,我們存儲過字串和數(包括整數和浮點數)。有時候可以把一堆東西存儲在一起,放在某種或者集合中,這可能很有用。這樣一來,就可以一次對整個集合做某些處理,也能更容易地記錄一組東西。有一類集合叫做清單(list),另一類叫做字典(dictionary)。在這一章中,我們就來學習清單和字典的相關知識——什麼是列表,如何創建、修改和使用列表。

清單非常有用,很多很多程式裡都用到了清單。後面幾章開始討論圖形和遊戲程式設計時我們的例子中就會大量使用列表,因為遊戲中的很多圖形物件通常都存儲在清單中。


 12.1 什麼是列表

如果我讓你建一個家庭成員列表,你可能會像下圖這樣寫:


Python 中,就要寫成:

family = ['Mom', 'Dad', 'Junior', 'Baby']

 

如果我讓你寫下你的幸運數字,你可能會這樣寫:


Python 中,就要寫成:

luckyNumbers = [2, 7, 14, 26, 30]

 

family  luckyNumbers 都是 Python 列表的例子,清單中的單個元素就叫做項或者元素(item)。可以看到,Python 中的列表與你在日常生活中建立的列表並沒有太大差異。列表使用中括弧來指出從哪裡開始,到哪裡結束,另外用逗號分隔列表內的各項。


 12.2 創建列表

family  luckyNumbers 都是變數。前面曾經說過,可以為變數賦不同類型的值。我們已經為變數賦過數和字串,還可以為變數賦一個清單。

就像創建任何其他變數一樣,創建清單也是要為它賦某個值,如前面對 luckyNumbers 的賦值。另外還可以創建一個空的列表,如下:

newList = []

 

中括弧沒有任何元素,所以這個清單是空的。不過一個空列表有什麼用呢?為什麼想要創建這樣一個空列表呢?

嗯,很多情況下,我們無法提前知道列表中會有些什麼。我們不知道其中會有多少元素,也不知道這些元素是什麼,只知道將會用一個清單來保存這些內容。有了空清單後,程式就可以向這個清單中增加元素。這又怎麼做到呢?


 12.3 向清單增加元素

要向清單增加元素,需要使用 append()。在交互模式中試試下面的代碼:


你會得到這樣的結果:

['David']

 

再來增加一個元素:

>>> friends.append('Mary')

>>> print friends

['David', 'Mary']

 

記住,向清單增加元素之前,必須先創建列表(可以是空列表,也可以非空)。這就像在做一個蛋糕:不能直接把各種配料倒在一起,而是先將配料倒入碗中,不然肯定會弄得到處都是!

 


 

12.4 這個點是什麼

為什麼要在 friends  append() 之間 加一個點(.)呢?嗯,現在要談到一個重要的話題了:這就是物件。我們會在第 14 章學習關於物件的更多內容,不過現在先簡單解釋一下。

術語箱

追加(append)是指把一個東西加在最後面。

把一個東西追加到列表時,會把它增加到列表的末尾。

Python 中的很多東西都是物件(object)。要想用物件做某種處理,需要這個物件的名字(變數名),然後是一個點,再後面是要對物件做的操作。所以要向 friends 清單追加一個元素,就要寫成:

friends.append(something)

 

12.5 清單可以包含任何內容

清單可以包含 Python 能存儲的任何類型的資料,這包括數位、字串、物件,甚至可以包含其他列表。並不要求清單中的元素是同種類型或同一種東西。這說明,一個列表中可以同時包含不同類型,例如數位和字串,可能像這樣:

my_list = [5, 10, 23.76, 'Hello', myTeacher, 7, another_list]

 

下面用一些簡單的內容建立一個新清單,比如字母表中的字母,這樣我們在學習列表時就能更容易地瞭解做了些什麼。在交互模式中鍵入下面的代碼:

>>> letters = ['a', 'b', 'c', 'd', 'e']

 

12.6 從清單獲取元素

可以按元素的索引(index)號從清單獲取單個元素。清單索引從 0 開始,所以這個列表中的第一項就是 letters[0]

>>> print letters[0]

a

 

再來試一個:

>>> print letters[3]

d

 

為什麼索引從 0 而不是 1 開始?

從電腦發明到現在,很多程式師、工程師還有電腦科學家們一直都在爭論這個問題。我可不想陷入這場爭論中,所以直接告訴你答案:因為事實就是這樣。下面我們繼續……


好吧,好吧,可以看看下面的到底怎麼回事,這裡解釋了為什麼索引從 0 而不是從 1 開始。

到底怎麼回事?


你應該記得電腦使用二進位數字也就是比特來存儲一切資訊。很久以前,這些比特非常貴重。每一個比特都必須精挑細選,還要靠毛驢從比特農場搬運……這只是開個玩笑。不過這些比特位確實很昂貴。

二進位計數從 0 開始。所以,為了最高效地使用比特位而沒有任何浪費,記憶體位置和清單索引也都從 0 開始。


你很快就會習慣從 0 開始索引,因為這在程式設計中相當常見。

注意!這個詞有意思!

索引(index)表示某個東西的位置。index 的複數形式是 indices(不過有些人也用 indexes 作為 index 的複數形式)。

如果你在隊伍中排在第 4 個,你在這個隊伍中的索引就是 4。不過,如果你是一個 Python 列表中的第 4 個人,索引則是 3,因為 Python 清單索引從 0 開始!

 

12.7 列表分片

還可以使用索引從清單一次獲取多個元素。這叫做列表分片(slicing)。

>>> print letters[1:4]

['b', 'c', 'd']

 

 for 迴圈中的 range() 類似,分片獲取元素時,會從第一個索引開始,不過在達到第二個索引之前停止。正是因為這個原因,前面的例子中我們只取回 3 項,而不是 4 項。要記住這一點,一種方法就是牢記取回的項數總是兩個索引數之差(4 - 1 = 3,所以取回 3 項)。


關於清單分片,還有一個重要的問題需要記住:對列表分片時取回的是另一個(通常更小的)列表。這個更小的列表稱為原列表的一個分片(slice)。原來的列表並沒有改變。這個分片是原清單的部分副本(copy)。

下面來看這有什麼不同:

>>> print letters[1]

b

>>> print letters[1:2]

['b']

 

在第一種情況下,我們取回一個元素。在第二種情況下,取回的是包含這個元素的一個清單。這個差別很微妙,但是你必須知道。在第一種情況下,我們使用了一個索引從清單得到一個元素。第二種情況下則是使用分片記法來得到清單的一個單元素分片(只包含一個元素的分片)。

要真正瞭解二者的區別,可以試試這些命令:

>>> print type(letters[1])

<type 'str'>

>>> print type(letters[1:2])

<type 'list'>

 

這裡分別顯示了兩個結果的類型(type),從中可以清楚地看出,前一種情況下得到了一個元素(這裡是一個字串),後一種情況下得到的是一個列表。

對列表分片時會得到一個較小的列表,這是原清單中元素的一個副本。這說明,可以修改這個分片,而原列表不會受到任何影響。

分片簡寫

使用分片時可以採用一些簡寫形式。即使採用這些簡寫,也不會減少太多鍵入。不過程式師總是很懶,所以他們會大量使用簡寫。我希望你知道這些簡寫是什麼,這樣當你在別人的代碼中看到這些簡寫時就能認出來,而且明白是什麼意思。這很重要,因為學習新的程式設計語言時(或者籠統地說,學習程式設計時),查看並且理解其他人的代碼是一種很好的方法。

如果你想要的分片包括清單的開始部分,簡寫方式是使用冒號,後面是想要的元素個數,例如:

>>> print letters[:2]

['a', 'b']

 

注意,冒號前面沒有數字。這樣就會得到從列表起始位置開始一直到(但不包括)指定索引之間的所有元素。

要得到列表末尾也可以用類似的記法。

>>> print letters[2:]

['c', 'd', 'e']

 

使用一個後面跟冒號的數,這樣可以得到從指定索引到清單末尾的所有元素。

如果沒有放入任何數,而只有冒號,就可以得到整個列表:

>>> print letters[:]

['a', 'b', 'c', 'd', 'e']

 

應該記得吧?我說過分片就是建立原列表的副本。所以 letters[:] 會建立整個列表的副本。如果你想對列表做些修改,但是同時還想保持原來的列表不做任何改變,使用這種分片就會很方便。

 

12.8 修改元素

可以使用索引來修改某個清單元素:

>>> print letters

['a', 'b', 'c', 'd', 'e']

>>> letters[2] = 'z'

>>> print letters

['a', 'b', 'z', 'd', 'e']

 

但是不能使用索引向清單增加新的元素。目前,這個列表中有 5 項,索引分別是從 0 4

所以不能這樣做:

letters[5] = 'f'

 

這是不行的。(如果你願意也可以試試看。)這就像是想要改變一個還不存在的東西。要向清單中增加元素,必須另想其他辦法,我們下面就會做這個工作。不過,在此之前,先把列表改回到原來的樣子:

>>> letters[2] = 'c'

>>> print letters

['a', 'b', 'c', 'd', 'e']

 

12.9 向清單增加元素的其他方法

我們已經看到了如何使用 append() 向清單增加元素。不過除此以外還有其他一些方法。實際上,向清單增加元素共有 3 種方法:append()extend()  insert()

·    append() 向清單末尾增加一個元素。

·    extend() 向清單末尾增加多個元素。

·    insert() 在清單中的某個位置增加一個元素,不一定非得在清單末尾。你可以告訴它要在哪裡增加元素。

增加到列表末尾:append()

我們已經見過 append() 是如何工作的。它把一個元素增加到清單末尾:

>>> letters.append('n')

>>> print letters

['a', 'b', 'c', 'd', 'e', 'n']

 

再來增加一項:

>>> letters.append('g')

>>> print letters

['a', 'b', 'c', 'd', 'e', 'n', 'g']

 

注意這些字母並沒有按順序排列。這是因為 append() 只是將元素增加到清單末尾。如果希望這些元素按順序排列,必須對它們排序。稍後就會談到排序。

擴展列表:extend()

extend() 在清單末尾增加多個元素:

>>> letters.extend(['p', 'q', 'r'])

>>> print letters

['a', 'b', 'c', 'd', 'e', 'n', 'g', 'p', 'q', 'r']

 

注意 extend() 方法的圓括號中是一個列表。列表有一個中括弧,所以對於 extend(),可以同時有圓括號和中括弧。

提供給 extend() 的清單中的所有內容都會增加到原清單的末尾。

插入一個元素:insert()

insert() 會在列表中的某個位置增加一個元素。可以指定希望將元素增加到清單的哪個位置:

>>> letters.insert(2, 'z')

>>> print letters

['a', 'b', 'z', 'c', 'd', 'e', 'n', 'g', 'p', 'q', 'r']

 

在這裡,我們將字母 z 增加到索引為 2 的位置。索引 2 是清單中的第 3 個位置(因為索引從 0 開始)。原先位於第 3 個位置上的字母(也就是 c)會向後推一個位置,移到第 4 個位置上。它後面的每一個元素也都要向後移一個位置。

append()  extend() 的區別

有時 append()  extend() 看起來很類似,不過它們確實有一些區別。下面再回到原來的列表。首先,用 extend() 增加 3 個元素:

>>> letters = ['a','b','c','d','e']

>>> letters.extend(['f', 'g', 'h'])

>>> print letters

['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']

 

現在,再用 append() 做同樣的事情:

>>> letters = ['a', 'b', 'c', 'd', 'e']

>>> letters.append(['f', 'g', 'h'])

>>> print letters

['a', 'b', 'c', 'd', 'e', ['f', 'g', 'h']]

 

怎麼回事?嗯,我們前面說過,append() 會向清單增加一個元素。它怎麼會增加 3 個元素呢?其實它並沒有增加 3 個元素,這裡確實只增加了一個元素,只不過這剛好是一個包含 3 項的列表。正是這個原因,所以在這個列表中多了一對中括弧。要記住,列表可以包含任何東西,也包括其他列表。這個例子就屬於這種情況。

insert() 的工作與 append() 相同,只不過你可以告訴它在哪裡放入新的元素。 append() 總是把新元素放在清單末尾。

 

12.10 從清單刪除元素

如何從清單刪除或者去除元素呢?有 3 種方法:remove()del  pop()

 remove() 刪除元素

remove() 會從列表中刪除你選擇的元素,把它丟掉:

>>> letters = ['a', 'b', 'c', 'd', 'e']

>>> letters.remove('c')

>>> print letters

['a', 'b', 'd', 'e']

 

你不需要知道這個元素在清單中的具體位置,只需要知道它確實在列表中(可以是任何位置)。如果你想刪除的東西根本不在列表中,就會得到錯誤消息:

>>> letters.remove('f')

Traceback (most recent call last):

  File "<pyshell#32>", line 1, in <module>

    letters. remove('f')

ValueError: list.remove(x): x not in list

 

那麼怎麼才能知道清單中是否包含某個元素呢?後面就要講到。先來看另外兩種從清單中刪除元素的方法。

 del 刪除

del 允許利用索引從清單中刪除元素,如下所示:

>>> letters = ['a', 'b', 'c', 'd', 'e']

>>> del letters[3]

>>> print letters

['a', 'b', 'c', 'e']

 

在這裡,我們刪除了第 4 個元素(索引 3),也就是字母 d

 pop() 刪除元素

pop() 從清單中取出最後一個元素交給你。這說明,你可以為它指派一個名字,比如:

>>> letters = ['a', 'b', 'c', 'd', 'e']

>>> lastLetter = letters.pop()

>>> print letters

['a', 'b', 'c', 'd']

>>> print lastLetter

e

 

使用 pop() 時還可以提供一個索引,如:

>>> letters = ['a', 'b', 'c', 'd', 'e']

>>> second = letters.pop(1)

>>> print second

b

>>> print letters

['a', 'c', 'd', 'e']

 

在這裡我們彈出了第 2 個字母(索引 1),也就是 b。彈出的元素賦給 second,而且會從 letters 刪除。

括弧裡沒有提供參數時,pop() 會返回最後一個元素,並把它從列表中刪除。如果在括弧裡放入一個數,pop(n) 會給出這個索引位置上的元素,而且會把它從列表中刪除。


12.11 搜索列表

清單中有多個元素時,怎麼查找這些元素呢?對列表通常有兩種處理:

·    查找元素是否在清單中;

·    查找元素在清單中的哪個位置(元素的索引)。

in 關鍵字

要找出某個元素是否在清單中,可以使用 in 關鍵字,例如:

if 'a' in letters:

    print "found 'a' in letters"

else:

    print "didn't find 'a' in letters"

 

'a' in letters 部分是一個布林或邏輯運算式。如果 a 在這個列表中,它會返回值 True,否則返回 False

術語箱

布林(boolean)是一種只使用兩個值(1 0,或者 true false)的算數運算。這是數學家喬治 · 布林發明的,用 andor  not 來結合 true false 條件(由 1 0 表示)時,就會用到布耳運算,我們在第 7 章中已經見過。

可以在交互模式中試試下面的命令:

>>> 'a' in letters

True

>>> 's' in letters

False

 

可以看到,名為 letters 的清單中確實包含一個元素 a,但是不包含元素 s。所以 a 在列表中,而 s 不在列表中。現在可以結合使用 in  remove() 編寫一些代碼,保證即使值不在列表中也不會給出錯誤:

if 'a' in letters:

    letters.remove('a')

 

查找索引

為了找出一個元素位於清單中的什麼位置,可以使用 index() 方法,如下:

>>> letters = ['a', 'b', 'c', 'd', 'e']

>>> print letters.index('d')

3

 

所以我們知道 d 的索引是 3,這說明它是列表中的第 4 個元素。

就像 remove() 一樣,如果在列表中沒有找到這個值,index() 會給出一個錯誤,所以最好結合使用 in,就像這樣:

if 'd' in letters:

    print letters.index('d')

 

 12.12 迴圈處理列表

最早開始討論迴圈時,我們看到迴圈完成了一個值列表的反覆運算處理。我們還瞭解了 range() 函數,並用它作為快捷方式為迴圈生成數位清單。前面已經看到 range() 確實可以提供一個數字清單。

不過迴圈完全可以反覆運算處理任何列表,而不一定非得是數位清單。假設要顯示出我們的字母清單,一行顯示一個元素,可以這樣做:

>>> letters = ['a', 'b', 'c', 'd', 'e']

>>> for letter in letters:

    print letter

a

b

c

d

e

 

這裡我們的迴圈變數是 letter。(之前我們使用了 looper  ij  k 之類的迴圈變數。)迴圈反覆運算處理(迴圈處理)列表中的所有值,每次反覆運算時,當前元素會存儲在迴圈變數 letter 中,然後顯示出來。


 12.13 列表排序

清單是一種有順序(ordered)的集合 。這說明清單中的元素有某種順序,每個元素都有一個位置,也就是它的索引。一旦以某種順序將元素放在清單中,它們就會保持這種順序,除非用 insert()append()remove()  pop() 改變列表。不過這個順序可能不是你真正想要的順序。你可能希望列表在使用前已經排序。

要對列表排序,可以使用 sort() 方法。

>>> letters = ['d', 'a', 'e', 'c', 'b']

>>> print letters

['d', 'a', 'e', 'c', 'b']

>>> letters.sort()

>>> print letters

['a', 'b', 'c', 'd', 'e']

 

sort() 會自動按字母順序對字串從小到大排序,如果是數位,就會按數位順序從小到大排序。

有一點很重要,你要知道 sort() 會在原地修改列表。這說明它會改變你提供的原始列表,而不是創建一個新的有序列表。所以,你不能這樣做:

>>> print letters.sort()

 

如果這樣做,會得到“None”。必須分兩步來完成,就像這樣:

>>> letters.sort()

>>> print letters

 

按逆序排序

讓一個列表按逆序排序有兩種方法。一種方法是先按正常方式對清單排序,然後對這個有序列表完成逆置(reverse),如下:

>>> letters = ['d', 'a', 'e', 'c', 'b']

>>> letters.sort()

>>> print letters

['a', 'b', 'c', 'd', 'e']

>>> letters.reverse()

>>> print letters

['e', 'd', 'c', 'b', 'a']

 

在這裡我們看到一個新的列表方法 reverse(),它會把清單中元素的順序倒過來。

另一種方法是向 sort() 增加了一個參數,直接讓它按降冪排序(從大到小):

>>> letters = ['d', 'a', 'e', 'c', 'b']

>>> letters.sort (reverse = True)

>>> print letters

['e', 'd', 'c', 'b', 'a']

 

這個參數名為 reverse,它會按照你的意願,將列表按逆序排序。

要記住,我們剛才討論的所有排序和逆置都會對原來的清單做出修改。這說明,你原來的列表已經沒有了。如果希望保留原來的順序,而對清單的副本進行排序,可以使用分片記法建立副本,也就是與原列表相等的另一個清單(有關的內容已經在這一章前面討論過):

>>> original_list = ['Tom', 'James', 'Sarah', 'Fred']

>>> new_list = original_list[:]

>>> new_list.sort()

>>> print original_list

['Tom', 'James', 'Sarah', 'Fred']

>>> print new_list

['Fred', 'James', 'Sarah', 'Tom']

 


卡特,很高興你問這個問題。如果你還記得很早很早以前我們剛開始談到名字和變數時(第 2 ),曾經說過,完成 name1 = name2 之類的操作時,就是為同一個東西建立一個新的名字。應該還記得這個圖:


所以為一個東西指定另一個名字時,只是向同一個東西增加一個新的標籤。在卡特的這個例子中,new_list  original_list 都表示同一個列表。可以用任何一個名字來改變列表(例如,可以對它排序)。不過,這裡仍然只有一個列表,就如:


我們對 new 完成排序,但是 original 也同樣得到排序,因為 new  original 只是同一個列表的兩個不同名字。這裡並沒有兩個不同的列表。

當然,也可以把 new 標籤移到一個全新的列表上,就像這樣:


2 章對字串和數就是這樣做的。

這說明,如果你確實想建立一個列表的副本,就要另想辦法,而不能只是用 new = original。要達到這個目的,最容易的方法是使用分片記法,就像前面所做的:new = original[:]。這表示複製清單中的所有內容,從第一個元素到最後一個元素。這樣就可以得到:


這裡有兩個不同的列表。我們建立了原列表的副本,命名為 new。現在如果對一個列表排序,另一個列表將不會同時排序。

另一種排序方法——sorted()

還有一種方法可以得到一個列表的有序副本而不會影響原清單的順序。Python 提供了一個名為 sorted() 的函數可以完成這個功能。它的工作如下:

>>> original = [5, 2, 3, 1, 4]

>>> newer = sorted(original)

>>> print original

[5, 2, 3, 1, 4]

>>> print newer

[1, 2, 3, 4, 5]

 

sorted() 函數提供了原清單的一個有序副本。


 12.14 可改變和不可改變

如果還記得第 2 章中的內容,我們說過,真正改變一個數或字串是做不到的,你能改變的只是把一個名字指派到哪個數或字串(換句話說,你只能移動標籤)。不過,Python 中確實有一些可以改變的類型,列表就是其中之一。剛才已經看到,清單可以追加或刪除元素,另外清單中的元素還可以排序或逆置。

這兩種不同的變數分別稱為可改變和不可改變的變數。可改變(mutable)是指能夠改變或者可以改變。不可改變(immutable)表示不能改變或者不可以改變。在 Python 中,數位和字串是不可改變的(不能改變),而列表是可改變的(能夠改變)。

元組——不可改變的列表

有些情況下你可能不希望列表可以改變。Python 中有沒有一種不可改變的列表呢?答案是肯定的。確實有一個名為元組(tuple)的類型,這就屬於不可改變的列表。可以這樣來建立元組:

my_tuple = ("red", "green", "blue")

 

這裡使用了圓括號,而不是列表使用的中括弧。

由於元組是不可改變的,所以不能對元組完成排序,也不能追加和刪除元素。一旦用一組元素創建一個元組,它就會一直保持不變。


 12.15 雙重列表:資料表

考慮資料如何存儲在程式中時,可以用圖直觀地表示,這很有用。

變數有一個值。


列表就像是把一行值串在一起。


有時還需要一個包含行和列的表。


如何保存資料表呢?我們已經知道,清單中包含多個元素,可以把每個學生的成績放在一個列表中,像這樣:

>>> joeMarks = [55, 63, 77, 81]

>>> tomMarks = [65, 61, 67, 72]

>>> bethMarks = [97, 95, 92, 88]

 

或者對應每個課程使用一個列表,如下:

>>> mathMarks = [55, 65, 97]

>>> scienceMarks = [63, 61, 95]

>>> readingMarks = [77, 67, 92]

>>> spellingMarks = [81, 72, 88]

 

不過我們可能希望把所有資料都收集到一個資料結構中。

術語箱

資料結構(data structure)是一種在程式中收集、存儲或表示資料的方法。資料結構包括變數、清單和其他一些我們還沒有討論到的內容。實際上,資料結構這個詞就表示程式中資料的組織方式。

要為我們的成績建立一個資料結構,可以這樣做:

>>> classMarks = [joeMarks, tomMarks, bethMarks]

>>> print classMarks

[[55, 63, 77, 81], [65, 61, 67, 72], [97, 95, 92, 88]]

 

這會得到一個元素清單,其中每個元素本身又是一個清單。我們創建了一個列表的列表list of list),也就是雙重列表。classMarks 清單中的每個元素本身又都是一個清單。

還可以直接創建 classMarks,而不需要先創建 joeMarkstomMarks  bethMarks,如下:

>>> classMarks = [ [55,63,77,81], [65,61,67,72], [97,95,92,88] ]

>>> print classMarks

[[55, 63, 77, 81], [65, 61, 67, 72], [97, 95, 92, 88]]

 

現在來顯示我們的資料結構:classMarks  3 個元素,每個元素分別對應一個學生。所以可以使用 in 來迴圈處理:

>>> for studentMarks in classMarks:

        print studentMarks

 

[55, 63, 77, 81]

[65, 61, 67, 72]

[97, 95, 92, 88]

 

這裡我們對名為 classMarks 的列表完成迴圈處理。迴圈變數是 studentMarks。每次迴圈時,會列印清單中的一個元素。這裡的每一個元素分別是一個學生的成績,它本身也是一個列表。(前面創建過這些學生列表。)

可以注意到,這看上去與前一頁的表很類似,所以我們提出的這種資料結構可以把所有資料都保存在一個地方。

從表獲取一個值

怎麼得到這個表(也就是雙重列表)中的值呢?我們已經知道,第一個學生的成績(joeMarks)在一個列表中,而這個列表本身是 classMarks 中的第一個元素。

下面來檢查一下:

>>> print classMarks[0]

[55, 63, 77, 81]

 


classMarks[0]  Joe 4 門課程成績的一個列表。現在我們想從 classMarks[0] 得到一個值。怎麼做呢?可以使用第二個索引。

如果希望得到他的第三個成績(閱讀課成績),也就是索引 2,可以這樣做:

>>> print classMarks[0][2]

77

 

這會給出 classMarks 中的第一個元素(索引 0),也就是 Joe 的成績列表,以及這個清單中的第三個元素(索引 2),這正是他的閱讀課成績。看到一個名字後面帶著兩組中括弧時,比如說 classMarks[0][2],這往往表示一個雙重列表。

classMarks 列表並不知道 JoeTom Beth 這些名字,也不知道數學(Math)、科學(Science)、閱讀(Reading)和拼寫(Spelling)這些課程。這裡之所以這樣標,是因為我們知道想要在這個清單中儲存什麼資訊。不過,對於 Python 來說,它們只是列表中一些已經編號的位置而已。這就像郵局裡編號的郵箱。郵箱上沒有名字,只有編號。郵遞員只負責明確哪封信歸哪個郵箱,而你知道哪個郵箱是你的。


要對 classMarks 表加標籤,一種更準確的方法應該是這樣:


現在可以更容易地看出成績 77 存儲在 classMarks[0][2] 中。

如果編寫一個程式使用 classMarks 存儲我們的資料,就必須知道哪些資料存儲在哪一行哪一列。就像郵遞員一樣,我們的任務是明確哪個位置屬於哪個資料。


 12.16 字典

從上文可以看出,Python 清單是一種將元素組織在一起的方式。在程式設計中,你會經常想以另一種方式組織元素,即將某個值和另一個值關聯起來。這種組織方式就像電話通訊錄將姓名和電話號碼關聯起來,也像字典將單詞和它們的含義關聯起來。

Python 字典(dictionary)是一種將兩個東西關聯在一起的方式。被關聯在一起的兩個東西分別稱為鍵(key)和值(value)。字典中的每個項(item)或條目(entry)都有一個鍵和一個值,它們合起來被稱為鍵值對(key-value pair)。一個字典就是一些鍵值對的集合。

一個簡單的例子就是電話通訊錄。假設你想保存朋友們的電話號碼。你會使用他們的姓名去查找他們的號碼(希望你的朋友們沒有重名的)。這個姓名就是,即你會用它來查找資訊,而電話號碼就是,即你要查找的資訊。


下面是創建一個 Python 字典的方法,我們用它來存儲姓名和電話號碼。首先,創建一個空的字典:

>>> phoneNumbers = {}

 

這個代碼看起來和創建清單的方式非常像,只不過它使用的是花括弧而不是方括號。

然後,我們來添加一個條目:

>>> phoneNumbers["John"] = "555-1234"

 

如果我們列印一下字典,它看起來是這樣的:

>>> print phoneNumbers

{'John': '555-1234'}

 

首先顯示鍵,然後是一個冒號,再接著顯示值。之所以用引號,是因為在這個例子中鍵和值剛好都是字串(不是必需的)。

也可以用另一種方式來完成:

>>> phoneNumbers = {"John": "555-1234"}

 

我們來添加更多的條目。不像在清單中可以使用 append(),在字典中則沒有用於添加新條目的方法。只需要指定新的鍵和值就行了:

>>> phoneNumbers["Mary"] = "555-6789"

>>> phoneNumbers["Bob"] = "444-4321"

>>> phoneNumbers["Jenny"] = "867-5309"

 

我們來看一下整個字典:

>>> print phoneNumbers

{'Bob': '444-4321', 'John': '555-1234', 'Mary': '555-6789', 'Jenny': '867-5309'}

 

我們之所以創建字典,是因為我們可以在字典中查找東西。在這個例子中,我 們希望按姓名來查找電話號碼。你可以這樣做:

>>> print phoneNumbers["Mary"]

'555-6789'

 

注意,這裡使用方括號來指定你想要找的條目的鍵。整個字典本身還是被包裹在花括弧中。

字典和清單有點類似,但也有一些主要區別。這兩種類型都稱為集合collection),也就是說,它們都是將其他類型條目集中在一起的組織方式。

它們的一些相似點:

·    清單和字典都可以包含任意類型(甚至包括清單和字典)的條目,所以你可以有一個包含數位、字串、物件甚至其他集合的集合。

·    清單和字典都提供了在集合中查找條目的方法。

它們的一些不同點:

·    清單是有順序的(ordered)。如果你按照某種順序向清單中添加元素,這些元素就會保持這種順序。你還可以對列表進行排序。字典是無序的(unordered)。如果你向字典中添加內容,然後列印出來,顯示的順序可能會跟添加的順序不同。

·    清單中的元素是使用索引訪問的,而字典中的條目是使用鍵來訪問的:

·       >>> print myList[3]

·       'eggs'

·       >>> print myDictionary["John"]

·       '555-1234'

·        

前面提到過,在 Python 中很多東西都是物件,清單和字典也是。所以清單和字典都有一些使用點號標記法來使用的方法。

keys() 方法會列出字典中所有的鍵:

>>> phoneNumbers.keys()

['Bob', 'John', 'Mary', 'Jenny']

 

values() 方法會列出字典中所有的值:

>>> phoneNumbers.values()

['444-4321', '555-1234', '555-6789', '867-5309']

 

其他的語言也有與 Python 字典類似的東西。它們通常被稱為關聯陣列(associative array),因為它們將鍵和值關聯在一起。你可能也會聽說它們的另外一個名字——雜湊表(hash table)。

和清單一樣,字典中的條目也可以是任意類型,包括簡單類型(整數、浮點數、字串)和集合(清單、字典)以及複合類型(物件)。

沒錯,你可以在字典中包含其他的字典,正如清單中可以包含其他的列表一樣。但事實上,這句話並不完全對。只有字典中的值是可以使用字典的,而鍵的要求更為嚴格一些。早先我們談論過可變類型(mutable)與不可變類型(immutable)。字典的鍵只可以使用不可變類型(布林、整數、浮點數、字串和元組)。你不能使用一個清單或者字典來當作鍵,因為它們是可變類型。

我在上面提到過,字典和清單有一個不同之處,就是字典是無序的。注意,儘管 Bob 的電話是第三個被添加到字典中的,但在列印字典內容時卻是第一個顯示的。字典沒有順序這個概念,所以對字典進行排序是沒有意義的。但有時你希望將字典中的內容按照某種順序顯示出來。雖然字典沒有順序,但可以對清單排序,所以當你拿到鍵的列表後,就可以對鍵進行排序,然後按照鍵的順序顯示字典內容。你可以使用 sorted() 函數來對字典的鍵排序,像下面這樣:

>>> for key in sorted(phoneNumbers.keys()):

        print key, phoneNumbers[key]

 

Bob 444-4321

Jenny 867-5309

John 555-1234

Mary 555-6789

 

這和你在列表中看到的 sorted() 函數是一樣的。如果你細想一下,會發現它是有效的,因為字典的鍵的集合是一個清單。

那麼,如果你想將字典的值(而不是鍵)按某種順序輸出該怎麼辦呢?在電話通訊錄的例子中,就是按照電話號碼從小到大輸出。字典的查找過程是單向的,這意味著只能用鍵去查找值,而不能反過來。所以對值排序會有些困難。但這仍然是可以做到的,只是需要做更多的工作:

>>> for value in sorted(phoneNumbers.values()):

        for key in phoneNumbers.keys():

            if phoneNumbers[key] == value:

                print key, phoneNumbers[key]

Bob 444-4321

John 555-1234

Mary 555-6789

Jenny 867-5309

 

我們首先取得排序之後的值的列表,然後針對列表中的每個值,迴圈遍歷字典中的所有鍵,直到找到與該值關聯的鍵。

下面是字典可以做的其他一些事情。

·    使用 del 刪除一個條目:

·       >>> del phoneNumbers["John"]

·       >>> print phoneNumbers

·       {'Bob': '444-4321', 'Mary': '555-6789', 'Jenny': '867-5309'

·    使用 clear() 刪除所有條目(清空字典):

·       >>> phoneNumbers.clear()

·       >>> print phoneNumbers

·       {}

·    使用 in 確定某個鍵在字典中是否存在:

·       >>> phoneNumbers = {'Bob':'444-4321', 'Mary':'555-6789','Jenny':'867-5309'}

·       >>> "Bob" in phoneNumbers

·       True

·       >>> "Barb" in phoneNumbers

·       False

·        

字典在 Python 代碼中使用得很廣泛。

這些當然不是有關 Python 字典的全部內容,但你應該對其有了大致的瞭解,這樣你就可以在代碼中使用字典,也可以認出在其他代碼中出現的字典。

你學到了什麼

在這一章,你學到了以下內容。

·    列表是什麼。

·    如何向清單中增加元素。

·    如何從清單刪除元素。

·    如何確定列表是否包含某個值。

·    如何對列表排序。

·    如何建立列表的副本。

·    元組。

·    雙重列表。

·    Python 字典。

測試題

1. 向清單增加元素有哪些方法?

2. 從清單刪除元素有哪些方法?

3. 要得到一個列表的有序副本,但又不能改變原來的列表,有哪兩種方法?

4. 怎樣得出某個值是否在列表中?

5. 如何確定某個值在列表中的位置?

6. 什麼是元組?

7. 如何建立雙重列表?

8. 如何從一個雙重列表中得到一個值?

9. 什麼是字典?

10. 如何向字典中增加項?

11. 怎樣使用鍵去查找一個條目?

動手試一試

1. 寫一個程式,讓使用者提供 5 個名字。程式要把這 5 個名字保存在一個列表中,最後列印出來。就像這樣:

Enter 5 names:

Tony

Paul

Nick

Michel

Kevin

The names are Tony Paul Nick Michel Kevin

 

2. 修改第 1 題的程式,要求不僅顯示原來的名字清單,還要顯示出排序後的清單。

3. 修改第 1 題的程式,要求只顯示使用者鍵入的第 3 個名字,就像這樣:

The third name you entered is: Nick

 

4. 修改第 1 題的程式,讓使用者替換其中一個名字。用戶應該能選擇要替換哪個名字,然後鍵入新名字。最後顯示這個新的清單:

Enter 5 names:

Tony

Paul

Nick

Michel

Kevin

The names are Tony Paul Nick Michel Kevin

Replace one name. Which one? (1-5): 4

New name: Peter

The names are Tony Paul Nick Peter Kevin

 

5. 編寫一個字典程式,讓使用者可以添加單詞和定義,然後可以查找這些單詞。確保當要查找的單詞不存在時,用戶能夠知曉。運行的時候,它應該是像這樣的:

Add or look up a word (a/l)? a

Type the word: computer

Type the definition: A machine that does very fast math

Word added!

Add or look up a word (a/l)? l

Type the word: computer

A machine that does very fast math

Add or look up a word (a/l)? l

Type the word: qwerty

That word isn't in the dictionary yet.

 

 

0 留言:

發佈留言