Python 入門指南 5.0

單元 9 - 序列、字典與集合

~~學習進度表~~

Python 中內建有非常多種的複合資料型態 (compound data type) ,所謂複合資料型態是指可以包含元素 (element) ,作為簡單資料結構 (data structure) 的資料型態 (data type) ,常用的包括序列 (sequence) 、字典 (dictionary) 與集合 (set)

Sequence Types
    list, tuple, range, str
Mapping Types
    dict
Set Types
    set

序列包括串列 (list) 、序對 (tuple) 、 range 及字串 (string) ,之所以統稱為序列是因爲這些型態有一樣的操作方式,如下表 st 表示序列, x 表示元素, nijk 都表示整數

運算說明
x in s判斷 x 是否在 s 中,如在回傳 True ,反之回傳 False
x not in s判斷 x 是否不在 s 中,如在回傳 True ,反之回傳 False
s + t將 s 與 t 連接在一起, s 與 t 必須是相同的型態
s * n, n * s將 s 複製 n 倍
s[i]取得索引值為 i 的元素
s[i:j]取得索引值 i 到 j-1 的子序列
s[i:j:k]取得索引值 i 到 j-1 間隔為 k 的子序列
len(s)回傳 s 的元素總數
min(s)回傳 s 中的最小值
max(s)回傳 s 中的最大值
s.index(x[, i[, j]])取得 x 在 s 中的第一個索引值,另可設定 i 之後到 j 之前
s.count(x)計算 x 在 s 中的個數

序列又分為可變 (mutable) 的與不可變的 (immutable) ,可變的就是可以改變元素內容,不可變的不能改變元素內容,以上提到的序列中,序對、 range 及字串都是不可變的,串列是可變的,所以串列多了以下操作

運算說明
s[i] = x將 s 中索引值為 i 的元素重新設定為 x
s[i:j] = t將 s 中索引值從 i 到 j-1 的元素重新設定為 t
del s[i:j]刪除 s 中索引值從 i 到 j-1 的元素
s[i:j:k] = t將 s 中索引值從 i 到 j-1 間隔為 k 的元素重新設定為 t
del s[i:j:k]刪除 s 中索引值從 i 到 j-1 間隔為 k 的元素
s.append(x)將 x 加入到 s 中
s.clear()清除 s 中的所有元素
s.copy()拷貝 s 中的所有元素並回傳新的序列
s.extend(t), s += t將 t 中的元素加入到 s 中
s *= n將 s 中的元素複製 n 倍
s.insert(i, x)將 x 加入到 s 中索引值為 i 位置
s.pop(), s.pop(i)回傳並移除 s 中最後一個或索引值 i 的元素
s.remove(x)移除 s 中的 x
s.reverse()倒轉 s 中到元素順序

數字類型態如整數、浮點數也都是不可變的,也就是不能改變內容的資料型態都是不可變的,而可以改變內容的資料型態都是可變的,像是字典或集合都是可變的。

Python 中最常用來處理資料的就是串列,以下用巢狀迴圈 (nested loop) 將九九乘法表的數字加入到二維串列中

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
# 設定空串列
demo = []

# 用巢狀迴圈替空串列建立資料
for i in range(1, 10):
    # 建立二維串列
    demo.append([])
    # 利用內層迴圈將九九乘法表加入到子串列中
    for j in range(1, 10):
        # 附加九九乘法表到子串列中
        demo[i - 1].append(i * j)

# 逐行印出九九乘法表
for i in demo:
    print(i)

# 檔名: list_demo01.py
# 說明:《Python入門指南》的範例程式
# 網站: http://kaiching.org
# 作者: 張凱慶
# 時間: 2023 年 5 月

第 2 行,串列的字面常數 (literal) 為中括弧,不加任何元素表示沒有元素,叫做空串列

 2
demo = []

串列的元素可以是任意型態的資料,元素個數也不受限。

第 7 行,利用串列的 append() 方法 (method) 附加空的子串列

07
    demo.append([])

第 11 行,繼續利用 append() 方法將九九乘法表的數字附加到子串列中,注意這裡的 i - 1 ,這是因為索引值從 0 開始

11
        demo[i - 1].append(i * j)

簡單說,第一層迴圈在串列中建立空的子串列,第二層迴圈再把然後把九九乘法表的數字加入到子串列中,串列的元素為子串列就稱作二維串列,上例執行結果如下

$ python list_demo01.py
[1, 2, 3, 4, 5, 6, 7, 8, 9]
[2, 4, 6, 8, 10, 12, 14, 16, 18]
[3, 6, 9, 12, 15, 18, 21, 24, 27]
[4, 8, 12, 16, 20, 24, 28, 32, 36]
[5, 10, 15, 20, 25, 30, 35, 40, 45]
[6, 12, 18, 24, 30, 36, 42, 48, 54]
[7, 14, 21, 28, 35, 42, 49, 56, 63]
[8, 16, 24, 32, 40, 48, 56, 64, 72]
[9, 18, 27, 36, 45, 54, 63, 72, 81]
$

這裡是直接印出串列的字面常數,因此頭尾都有串列的字面常數中括弧。

由於串列是 Python 最常運用的資料型態之一,因此在字面常數中有個簡化的寫法,上例可以改寫如下

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
# 設定空串列
demo = []

# 用巢狀迴圈替空串列建立資料
for i in range(1, 10):
    # 利用串列綜合運算附加九九乘法表到子串列中
    demo.append([i * j for j in range(1, 10)])

# 逐行印出九九乘法表
for i in demo:
    print(i)

# 檔名: list_demo02.py
# 說明:《Python入門指南》的範例程式
# 網站: http://kaiching.org
# 作者: 張凱慶
# 時間: 2023 年 5 月

第 7 行,這叫做串列的綜合運算 (list comprehension) ,也就是在串列的字面常數中用 for 迴圈,直接將 for 之前運算式得到的值,加入到串列中

 7
    demo.append([i * j for j in range(1, 10)])

序對是相對於串列的不可變資料型態,字面常數是小括弧,下面是把九九乘法表改成儲存到序對中

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 設定空串列
demo = []

# 用巢狀迴圈替空串列建立資料
for i in range(1, 10):
    # 利用串列綜合運算附加九九乘法表到子串列中
    demo.append([i * j for j in range(1, 10)])
    # 將子串列轉換成序對
    demo[i - 1] = tuple(demo[i - 1])

# 將串列轉換成序對
demo = tuple(demo)

# 逐行印出九九乘法表
for i in demo:
    print(i)

# 檔名: tuple_demo.py
# 說明:《Python入門指南》的範例程式
# 網站: http://kaiching.org
# 作者: 張凱慶
# 時間: 2023 年 5 月

由於序對是不可變的,並不能像串列一樣先建立空序對,再把元素加入序對中,因此這裡先用串列建立元素,然後第 9 行先把子串列用內建函數 tuple() 轉換成序對

 9
    demo[i - 1] = tuple(demo[i - 1])

第 12 行,再把外層串列轉換成序對

12
demo = tuple(demo)

執行結果如下

$ python tuple_demo.py
(1, 2, 3, 4, 5, 6, 7, 8, 9)
(2, 4, 6, 8, 10, 12, 14, 16, 18)
(3, 6, 9, 12, 15, 18, 21, 24, 27)
(4, 8, 12, 16, 20, 24, 28, 32, 36)
(5, 10, 15, 20, 25, 30, 35, 40, 45)
(6, 12, 18, 24, 30, 36, 42, 48, 54)
(7, 14, 21, 28, 35, 42, 49, 56, 63)
(8, 16, 24, 32, 40, 48, 56, 64, 72)
(9, 18, 27, 36, 45, 54, 63, 72, 81)
$

序對的好處是把資料變成唯讀,也就不需要擔心資料有可能被修改的問題。

字串就是文字資料,我們已經見過很多字串的例子,包括怎麼使用格式化字串。然而字串也是不可變的,這是說我們不能修改字串的內容,但是可以用重新指派的方式來處理字串,例如

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
# 建立字串變數 demo
demo = "Hello World!"

# 印出變數 demo
print(demo)

# 回傳替換子字串後的新字串
demo = demo.replace("World", "There")

# 印出變數 demo
print(demo)

# 檔名: string_demo.py
# 說明:《Python入門指南》的範例程式
# 網站: http://kaiching.org
# 作者: 張凱慶
# 時間: 2023 年 5 月

第 8 行,等號右邊變數 demo 呼叫 replace() 方法,這會回傳一個新字串,新字串中會把原本 demo 中的 "World" 替換成 "There" 利用等號也就是重新指派給變數 demodemo 就會變成新的字串

 8
demo = demo.replace("World", "There")

對 Python 而言, "Hello World!" 是一個字串物件, "Hello There!" 則是另外一個字串物件,重新指派的結果是把 demo 從連結 "Hello World!" 變成連結 "Hello There!" ,因此原本 "Hello World!" 字串物件的內容並沒有被改變,至於 demo 連結 "Hello There!" 後, "Hello World!" 並沒有連結到任何變數, "Hello World!" 這個字串物件就會被 Python 資源回收機制 (garbage collection) 消除,因此不需要擔心記憶體管理的問題。

執行結果如下

$ python string_demo.py
Hello World!
Hello There!
$

Python 集合的概念跟數學集合的概念一樣,集合中不會有重複的元素,因此可以被用來處理不會有重複的資料,例如名單,以下範例用集合處理兩份名單,每份名單只有四組編號

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 兩個名單集合
demo1 = {"A01", "A02", "B21", "D32"}
demo2 = {"A01", "B02", "B21", "D25"}

# 印出差集
print(demo1.difference(demo2))

# 將兩個名單合併
demo1 = demo1.union(demo2)
print(demo1)

# 剔除名單人選
for i in range(len(demo1) - 1):
    demo1.pop()
# 印出中選者
print(demo1)

# 檔名: set_demo.py
# 說明:《Python入門指南》的範例程式
# 網站: http://kaiching.org
# 作者: 張凱慶
# 時間: 2023 年 5 月

第 2 行及第 3 行建立兩個集合變數,集合的字面常數為大括弧

 2
 3
demo1 = {"A01", "A02", "B21", "D32"}
demo2 = {"A01", "B02", "B21", "D25"}

第 6 行,利用集合的 difference() 方法計算兩個集合的差集,也就是把兩個集合中非共通元素回傳一個新集合

 6
print(demo1.difference(demo2))

這裡是直接印出回傳的新集合。

第 9 行,利用集合的 union() 方法合併 demo1demo2 ,由於 union() 是回傳合併後的新集合,因此這裡重新指派給 demo1 ,然後第 10 行印出 demo1

 9
10
demo1 = demo1.union(demo2)
print(demo1)

第 13 行利用 for 迴圈執行到 demo1 元素總數減 1 ,然後第 14 行利用集合的 pop() 方法隨機去除一個元素

13
14
for i in range(len(demo1) - 1):
    demo1.pop()

嚴格說 pop() 方法並非隨機去除,而是集合並沒有排序,因此每一次執行得到的集合順序都不同,造成 pop() 去除的結果也不一樣。

最後第 16 行印出 demo1 中最後一個元素,此例執行結果如下

$ python set_demo.py
{'A02', 'D32'}
{'A02', 'B02', 'D25', 'A01', 'D32', 'B21'}
{'B21'}
$

字典是配對型的資料型態,字面常數同樣是大括弧,如下大括弧裡頭是冒號區隔的 keyvalue ,每一組 keyvalue 為一筆資料

  
d = {key:value}

字典中每一筆資料也是用逗號區隔,其中 key 必須是不可變的資料型態, value 則沒有限制。

下例用字典建立九九乘法表

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
# 設定空字典
demo = dict()

# 外層迴圈建立字典的 key
for i in range(1, 10):
    # 每個 value 設定為九九乘法表中的一列
    demo[i] = [i * j for j in range(1, 10)]

# 逐行印出九九乘法表
for i in demo:
    print(demo[i])

# 檔名: dictionary_demo.py
# 說明:《Python入門指南》的範例程式
# 網站: http://kaiching.org
# 作者: 張凱慶
# 時間: 2023 年 5 月

先看到第 2 行,這裡是用內建函數 dict() 建立空字典

 2
demo = dict()

其實空字典可以利用空的大括弧建立,倒是集合的字面常數的字面常數也是大括弧,空集合就只能用內建函數 set() 建立。

然後第一個 for 迴圈是在 demo 建立 19key ,每個 keyvalue 為串列,串列中儲存的就是九九乘法表的數字

 5
 7
for i in range(1, 10):
    demo[i] = [i * j for j in range(1, 10)]

第二個 for 迴圈印出九九乘法表,注意 for 迴圈用在字典 i 取得的是 key ,因此印出 value 要用索引值的方式

10
11
for i in demo:
    print(demo[i])

執行結果如下

$ python dictionary_demo.py
[1, 2, 3, 4, 5, 6, 7, 8, 9]
[2, 4, 6, 8, 10, 12, 14, 16, 18]
[3, 6, 9, 12, 15, 18, 21, 24, 27]
[4, 8, 12, 16, 20, 24, 28, 32, 36]
[5, 10, 15, 20, 25, 30, 35, 40, 45]
[6, 12, 18, 24, 30, 36, 42, 48, 54]
[7, 14, 21, 28, 35, 42, 49, 56, 63]
[8, 16, 24, 32, 40, 48, 56, 64, 72]
[9, 18, 27, 36, 45, 54, 63, 72, 81]
$

也可以把串列想像成 key0 開始遞增的字典,不過串列跟字典還是兩種完全不同的資料型態。

介紹完以上 Python 中常用的複合資料型態之後,接下來我們進入模組化 (modular design) 的第一步,也就是可藉由呼叫來重複執行的函數 (function) 。

中英文術語對照
複合資料型態compound data type
資料結構data structure
資料型態data type
字典dictionary
元素element
函數function
資源回收機制garbage collection
不可變的immutable
串列list
串列的綜合運算list comprehension
字面常數literal
方法method
模組化modular design
可變的mutable
巢狀迴圈nested loop
序列sequence
集合set
字串string
序對tuple
重點整理
1. Python 常用的複合資料型態包括序列、字典與集合。
2. 序列包括串列、序對、 range 及字串,序列對元素內容有一樣的操作方式,都是用索引值操作元素。
3. Python 的資料型態分成可變的及不可變的,可變的是指可以改變內容,不可變的是不能改變內容。
4. 字典是 key:value 配對型的資料型態, key 必須是不可變的資料型態。
5. 集合是不具有重複元素的資料型態,集合對元素是沒有排序的。
6. Python 對使用過的物件有資源回收機制,因此無須擔心記憶體管理問題。
問題與討論
1. 比較串列、字典、集合有什麼不一樣。
2. 為什麼要把資料型態分成可變與不可變?
3. 排序對具有多個元素的資料型態有何重要性?又要依據什麼條件來排序?
4. 什麼是資源回收機制?為什麼會有記憶體管理問題?
練習
1. 寫一個程式 exercise0901.py ,設定一個空串列變數,例如 a ,然後利用 append() 方法替 a 加入元素,像是 1"你好"[] ,最後用 for 迴圈逐一印出 a 中的元素。 參考程式碼
2. 承上題,將程式寫在 exercise0902.py 中,利用 if 判斷空串列 [] 是否存在於 a 中,如果存在就印出 "空串列存在" ,反之印出 "空串列不存在"參考程式碼
3. 承上題,將程式寫在 exercise0903.py 中,建立另一個串列例如 a2 ,元素可設定例如 [2, "Hello", {}] ,然後用加號連接兩個串列,最後印出 a[2:5] 的內容。 參考程式碼
4. 寫一個程式 exercise0904.py ,設定一個有 5 個不同大小數字的串列,然後用 min() 印出最小值, max() 印出最大值。 參考程式碼
5. 承上題,將程式寫在 exercise0905.py ,改成用 sorted() 對串列進行排序, sorted() 會回傳排序後的新串列,最後印出這個新串列。 參考程式碼
6. 承上題,將程式寫在 exercise0906.pysorted() 有個參數 reverse 如果設定為 True ,排序會改成由大排到小,最後印出倒轉大小順序的新串列。 參考程式碼
7. 寫一個程式 exercise0907.py ,建立一個有 5 個數字的串列,然後用關鍵字 del 隨意刪除兩個元素,最後印出剩下的元素。 參考程式碼
8. 承上題,將程式寫在 exercise0908.py ,改成用 remove() 方法移除元素, remove() 是直接提供元素值來移除。 參考程式碼
9. 寫一個程式 exercise0909.py ,建立有 4 個元素的集合變數,例如 a ,然後用 if 判斷任一元素是否存在,如果存在就印出 "存在" ,反之印出 "不存在"參考程式碼
10. 承上題,將程式寫在 exercise0910.py ,利用 add() 方法加入新元素到集合中,然後用 for 迴圈印出每個元素。 參考程式碼
11. 承上題,將程式寫在 exercise0911.py ,改成用 clear() 方法清空集合,然後用 if not 判斷是不是空集合,如果是空集合就印出 "這是空集合"參考程式碼
12. 寫一個程式 exercise0912.py ,建立一個字典變數,以數字為 key 加入至少三組 value ,然後用 sorted() 排序 key ,最後用 for 迴圈依據排序過的 key 印出 value參考程式碼
13. 承上題,將程式寫在 exercise0913.py ,利用關鍵字 del 刪除指定的 key ,再用新 key 新增元素,最後用 for 迴圈印出所有元素。 參考程式碼
14. 承上題,將程式寫在 exercise0914.py ,在 while True 迴圈中利用 popitem() 個別取出 keyvalue ,然後印出 value ,注意迴圈的結束條件為字典變為空字典的時候。 參考程式碼

上一頁 單元 8 - 例外處理
回 Python 入門指南 5.0 首頁
下一頁 單元 10 - 函數
回 Python 教材首頁
回程式語言教材首頁