Learning Objectives
- 串列(list)的宣告與運算:索引、切片、修改、結合、重複
- 名稱與物件:別名(b = a)與複製(a[:]、a.copy())的差別
- 串列相關的內建函數:len、max、min、sum、sorted、list
- 串列相關的方法:append、extend、insert、pop、remove、sort、reverse、index、count
- 串列生成式(List Comprehensions)
為什麼需要串列
一個班有 50 位同學的成績,總不能開 50 個變數 score1、score2……
串列(list)把一串資料裝進同一個容器,用一個名稱管理,再配合迴圈一次處理全部。
串列是 Python 用途最廣的資料型態:
| 特性 | 說明 | 和字串比較 |
|---|---|---|
| 有順序(sequence) | 每個項目有位置編號,可以用索引與切片 | 和字串一樣 |
| 可變(mutable) | 建立後可以修改、新增、刪除項目 | 字串不可變 |
| 項目型態可以不同 | ['ncu', 2015, 3.5, [1, 2]] |
字串只裝字元 |
「可變」帶來方便,也帶來本課最重要的觀念:兩個名稱可能指向同一個串列。改了其中一個,另一個也會跟著變。
建立、取用與修改串列
用中括號 [ ] 建立串列,項目之間用逗號分隔。注意第 7、9、11 行:串列可以原地修改,右邊物件圖裡是同一個串列的內容在變,list1 的箭頭一直指著它。
巢狀串列
串列的項目也可以是另一個串列。lst[2] 取出裡面的串列,lst[2][1] 再取出那個串列的索引 1。看物件圖:外層串列的第三格是一個箭頭,指向另一個串列。
想一想:索引與切片
下面的程式會印出什麼?
lst = list("Hello Python")
print(lst[6:-2])
下面的程式會印出什麼?
lst = [33, "python", [11, 12, 13], 77]
print(lst[2][-1], len(lst))
名稱與物件:別名
Module 02 說過:變數是名稱,指向記憶體裡的物件。寫 b = a 的時候,Python 不會複製串列,
只是讓 b 也指向 a 指著的那個串列。這時 a 和 b 是同一個物件的兩個名字,稱為別名(alias)。
| 寫法 | 結果 | 之後改 b,a 會變嗎? |
|---|---|---|
b = a |
兩個名稱、同一個串列 | 會 |
b = a[:] |
切片複製出新的串列 | 不會 |
b = a.copy() |
複製出新的串列(和 a[:] 相同) |
不會 |
b = list(a) |
用 a 的項目建立新的串列 |
不會 |
判斷是不是同一個物件,用 Module 04 的身分運算子 is。
b = a 與 b = a[:]
這是本課最重要的範例。第 3 行之後,看右邊:a 和 b 兩條箭頭指向同一個串列;第 4 行透過 b 加入 4,a 也看得到。第 10、11 行之後,c、d 各自指向新的串列,再怎麼改都不影響 a。
淺層複製:裡面的串列還是共用
copy() 和 [:] 只複製外層:新串列裡放的仍是指向原本那些內層串列的箭頭。看第 5 行:改 y[0],x[0] 也變了。要連內層一起複製,用 copy 模組的 deepcopy()。
想一想:別名與複製
下面的程式會印出什麼?
a = [1, 2]
b = a
b.append(3)
print(a)
下面的程式會印出什麼?
a = [1, 2]
b = a[:]
b.append(3)
print(a, b)
下面的程式會印出什麼?
a = [1, 2]
b = a
b = b + [3]
print(a)
串列的函數與方法
內建函數(寫成 函數(串列)):
| 函數 | 作用 |
|---|---|
len(lst) |
項目個數 |
max(lst) / min(lst) |
最大值/最小值(項目型態要能互相比較) |
sum(lst) |
總和(項目要是數值) |
sorted(lst, reverse=False) |
傳回新的排序後串列,原串列不變 |
list(seq) |
把字串、元組、range 等轉成串列 |
串列方法(寫成 串列.方法()),大多是原地修改串列本身:
| 方法 | 作用 |
|---|---|
append(x) |
把 x 加到最後 |
extend(t) |
把 t 的每個項目依序加到最後(相當於 lst += t) |
insert(i, x) |
把 x 插入到索引 i |
pop(i) |
取出並移除索引 i 的項目,預設是最後一個 |
remove(x) |
移除第一個等於 x 的項目 |
index(x) / count(x) |
x 第一次出現的位置/出現幾次 |
sort() / reverse() |
排序/反轉順序(傳回 None) |
copy() / clear() |
複製出新串列/清空 |
字串方法傳回新字串;串列方法多半直接改串列、傳回
None。所以nums = nums.sort()會讓nums變成None,這是常見的錯誤。
常用串列方法
每一行執行後看右邊的串列怎麼變。注意第 15 行:sort() 把串列排好了,但傳回的 r 是 None。
串列生成式
串列生成式 [運算式 for 項目 in 序列 if 條件] 很像數學的集合寫法,例如 S = {x² : x ∈ {0..5}}。比較第 2~4 行的 for 迴圈和第 8 行的生成式:做的是同一件事(迴圈變數叫 n 或 x 都可以,名稱自己取)。
想一想:方法與生成式
下面的程式會印出什麼?
nums = [3, 1, 2]
r = nums.sort()
print(r, nums)
下面的程式會印出什麼?
print([x * 2 for x in range(5) if x % 2 == 1])
實作:排名但保留原始順序
要把成績由高到低排名,但原本的 scores 不能被打亂。現在的程式有一個別名的問題:執行看看,「原始」那一行也被排序了。請完成兩個 TODO:讓 ranked 成為 scores 的複本,並用串列生成式找出 80 分以上的分數(依原始順序)。
重點精華回顧
- 串列的特性:有順序、可變、項目型態可以不同;用索引、切片取用與修改,
+結合、*重複、in檢查成員。 - 別名與複製:
b = a是同一個串列的兩個名稱;a[:]、a.copy()、list(a)才會複製出新串列。copy()是淺層複製,巢狀串列要用copy.deepcopy()。 - 內建函數:
len、max、min、sum、sorted(傳回新串列)、list。 - 串列方法:
append、extend、insert、pop、remove、index、count、sort、reverse;會修改串列的方法傳回None。 - 串列生成式:
[運算式 for 項目 in 序列 if 條件],用一行取代「空串列+for+append」。