課堂上常看到這種寫法:要記錄七位同學的期中考成績,於是開了七個變數。
score1 = 88
score2 = 72
score3 = 95
score4 = 60
score5 = 78
score6 = 83
score7 = 91要算平均,就得寫 (score1 + score2 + score3 + score4 + score5 + score6 + score7) / 7。這時如果轉學生來了、變成八個人,你得回頭改變數、改加總、改除數,三個地方都不能漏。班上有四十個人的話,這支程式基本上寫不下去。
串列(list)就是為了解決這件事存在的:一個變數裝一整排資料,要幾筆都行,加人減人不用改結構。這篇用同一份成績資料,把 list 最常用的操作一路練到能組出一支完整的統計程式。
本文程式碼實際在 Python 3.14.3 跑過(3.14 系列目前最新是 3.14.6,2026 年 6 月 10 日釋出),下面的語法在 Python 3.6 以上都通用,不用擔心版本。
建立串列,然後把資料取出來
scores = [88, 72, 95, 60, 78, 83, 91]
print(scores)
print(len(scores))[88, 72, 95, 60, 78, 83, 91]
7七個變數變成一個 scores,len(scores) 直接告訴你裡面有幾筆。有了這行,前面那個「改除數」的問題就不存在了——不管幾個人,平均都是 sum(scores) / len(scores)。
串列裡也可以放字串,型態甚至可以混著放(雖然實務上很少這樣用):
names = ['小明', '小華', '阿哲', '雅婷', '子豪', '欣怡', '建宏']空串列則寫成 data = [],之後再慢慢往裡面加東西,這在寫「邊跑邊收集結果」的程式時很常用。
索引:從 0 開始數,倒著數用負號
要拿出串列裡的某一筆,用中括號加位置編號:
scores = [88, 72, 95, 60, 78, 83, 91]
print(scores[0]) # 第一筆
print(scores[3]) # 第四筆
print(scores[-1]) # 最後一筆
print(scores[-2]) # 倒數第二筆88
60
91
83第一筆是 [0] 不是 [1],這是新手最容易算錯的地方。理解方式是把索引想成「從開頭走幾步」:[0] 走 0 步就是第一個,[3] 走 3 步剛好落在第四個。
負索引則是從尾巴倒著數,[-1] 永遠是最後一筆。這個寫法非常實用——想拿最後一個元素時,你不必先算出串列長度再減一(scores[len(scores)-1] 這種寫法能動,但沒人這樣寫)。
一定會遇到的坑:IndexError
scores = [88, 72, 95, 60, 78, 83, 91]
print(scores[7])IndexError: list index out of range七筆資料的合法索引是 0 到 6,[7] 已經超出範圍。看到 list index out of range 的第一直覺就該是:我是不是把「第幾個」直接當成索引用了? 七筆資料的最後一筆是 [6],不是 [7]。
切片:一次取出一整段
只想要前三名的成績、或後三筆,用切片 [起點:終點]:
scores = [88, 72, 95, 60, 78, 83, 91]
print(scores[0:3]) # 第 0、1、2 筆
print(scores[:3]) # 起點省略=從頭開始
print(scores[4:]) # 終點省略=一路到底
print(scores[-3:]) # 最後三筆
print(scores[::2]) # 每隔一個取一個[88, 72, 95]
[88, 72, 95]
[78, 83, 91]
[78, 83, 91]
[88, 95, 78, 91]切片最反直覺的一點:終點那個位置「不包含」。scores[0:3] 拿到的是索引 0、1、2 三筆,索引 3 不會進來。記法是「取到終點之前為止」,或者換個角度看——0:3 剛好拿到 3 筆,起點到終點的差就是筆數,算起來反而方便。
第三個數字是「間隔」,[::2] 表示從頭到尾、每兩個取一個,所以拿到索引 0、2、4、6 的成績。順帶一提,[::-1] 會把整個串列反過來,這是 Python 裡把 list 或字串反轉最短的寫法。
切片出來的是新的串列,原本的 scores 完全沒被動到,這點跟等一下會講的 sort() 剛好相反。
增刪改:五個方法各司其職
串列跟字串最大的差別是串列可以改(字串不行,這種特性叫 mutable)。直接對某個位置賦值就是修改:
scores = [88, 72, 95, 60, 78, 83, 91]
scores[3] = 65 # 雅婷補考後改成 65
print(scores)[88, 72, 95, 65, 78, 83, 91]新增和刪除則各有專門的方法,容易搞混的是它們「認的東西不一樣」:
scores = [88, 72, 95, 65, 78, 83, 91]
scores.append(70) # 加在最後面
print(scores)
scores.insert(0, 100) # 插進指定位置(第 0 位)
print(scores)
removed = scores.pop() # 拿掉最後一筆,並把它回傳
print(removed, scores)
scores.remove(95) # 依「值」刪除,刪掉 95 這個分數
print(scores)[88, 72, 95, 65, 78, 83, 91, 70]
[100, 88, 72, 95, 65, 78, 83, 91, 70]
70 [100, 88, 72, 95, 65, 78, 83, 91]
[100, 88, 72, 65, 78, 83, 91]分清楚這兩組就不會用錯:
pop(位置)認的是索引,pop()不給參數時預設拿掉最後一筆,而且會把拿掉的東西回傳給你,所以可以寫成last = scores.pop()。remove(值)認的是內容,你給它 60,它去找哪一筆等於 60 然後刪掉,回傳的是None。
remove() 還有一個要留意的行為:它只刪掉找到的第一個。
nums = [70, 85, 70, 90]
nums.remove(70)
print(nums)[85, 70, 90]兩個 70 只走了一個。如果你要清掉全部重複值,得用迴圈處理,不能指望一次 remove() 掃乾淨。
至於 del scores[0],效果跟 pop(0) 一樣是依位置刪,差別只在 del 不回傳被刪掉的值。兩個都能用,看你需不需要接住那筆資料。
走訪與統計:for、enumerate、四個內建函式
最單純的走訪是直接把每一筆丟給 for:
scores = [88, 72, 95, 60, 78, 83, 91]
for s in scores:
print(s, end=' ')
print()88 72 95 60 78 83 91 但實務上常常需要「第幾個」這個資訊,例如印成有編號的清單。這時候用 enumerate(),它會同時給你索引和值:
names = ['小明', '小華', '阿哲', '雅婷', '子豪', '欣怡', '建宏']
scores = [88, 72, 95, 60, 78, 83, 91]
for i, name in enumerate(names, start=1):
print(f'{i}. {name} {scores[i-1]} 分')1. 小明 88 分
2. 小華 72 分
3. 阿哲 95 分
4. 雅婷 60 分
5. 子豪 78 分
6. 欣怡 83 分
7. 建宏 91 分start=1 讓編號從 1 開始(給人看的清單通常從 1 數起),但要注意:編號從 1 開始,索引還是從 0 開始,所以取成績時是 scores[i-1] 不是 scores[i]。這行如果寫錯,成績就會整排錯位一格,而且不會報錯——只是每個人的分數都變成隔壁同學的,是那種很難看出來的 bug。
不想處理這個 offset 的話,另一種寫法是 for i in range(len(names)),索引和資料都用同一套 0 開始的編號,印出來時再寫 i+1 就好。兩種都常見。
統計四人組:len、sum、max、min
串列是數字時,這四個內建函式幾乎每支程式都會用到:
scores = [88, 72, 95, 60, 78, 83, 91]
print(len(scores)) # 幾筆
print(sum(scores)) # 總和
print(max(scores)) # 最大值
print(min(scores)) # 最小值
print(round(sum(scores) / len(scores), 1)) # 平均,取到小數第一位7
567
95
60
81.0平均沒有現成的內建函式,就是 sum() 除以 len()。round(數值, 1) 是四捨五入到小數第一位,不然算出來會是 81.0 這種還好,遇到除不盡的情況會拖出一長串小數。
排序:sorted() 和 sort() 差在哪
這兩個名字很像,行為卻不一樣,是很經典的混淆點:
scores = [88, 72, 95, 60, 78, 83, 91]
print(sorted(scores)) # 由小到大
print(sorted(scores, reverse=True)) # 由大到小
print(scores) # 原本的 scores 有變嗎?[60, 72, 78, 83, 88, 91, 95]
[95, 91, 88, 83, 78, 72, 60]
[88, 72, 95, 60, 78, 83, 91]sorted() 是內建函式,它回傳一個排好的新串列,原串列原封不動。
sort() 則是串列自己的方法,它直接把原串列就地排好,然後回傳 None:
scores = [88, 72, 95, 60, 78, 83, 91]
result = scores.sort()
print(result)
print(scores)None
[60, 72, 78, 83, 88, 91, 95]看到 result 印出 None 就是踩到這個坑了——scores = scores.sort() 這種寫法會讓你的資料整個變成 None,是新手很常犯的錯。原則很單純:想留著原本順序就用 sorted(),不需要原順序、想省一份記憶體就用 scores.sort()(單獨一行,不要接等號)。
隱形陷阱:b = a 不是複製
a = [88, 72, 95]
b = a
b[0] = 0
print(a, b)[0, 72, 95] [0, 72, 95]只動了 b,a 卻跟著變了。原因是 b = a 並沒有做出第二份資料,它只是讓 b 這個名字指向同一個串列,兩個名字綁在同一份資料上,從哪邊改都是改同一份。
要真的複製一份,用 .copy()(或切片 a[:],效果一樣):
a = [88, 72, 95]
b = a.copy()
b[0] = 0
print(a, b)[88, 72, 95] [0, 72, 95]這個陷阱在整數、字串上不會發生(那些型態改不了,一改就是換一個新的),只在 list、dict 這類可變型態才會咬人。之後你如果遇到「我明明只改了副本,原始資料怎麼也變了」的靈異現象,八成就是這裡。
合起來:一支成績統計程式
把前面所有東西串成一支完整、可以直接複製執行的程式:
names = ['小明', '小華', '阿哲', '雅婷', '子豪', '欣怡', '建宏']
scores = [88, 72, 95, 60, 78, 83, 91]
total = sum(scores)
average = total / len(scores)
print('{:*^24}'.format('期中考成績統計'))
for i, name in enumerate(names, start=1):
print(f'{i}. {name:<4} {scores[i-1]:>3} 分')
print(f'人數:{len(scores)} 人')
print(f'總分:{total} 分')
print(f'平均:{average:.1f} 分')
print(f'最高分:{max(scores)} 分({names[scores.index(max(scores))]})')
print(f'最低分:{min(scores)} 分({names[scores.index(min(scores))]})')
passed = []
failed = []
for i in range(len(scores)):
if scores[i] >= 70:
passed.append(names[i])
else:
failed.append(names[i])
print(f'及格 {len(passed)} 人:{passed}')
print(f'不及格 {len(failed)} 人:{failed}')
top3 = sorted(scores, reverse=True)[:3]
print(f'前三高分:{top3}')實際跑出來:
********期中考成績統計*********
1. 小明 88 分
2. 小華 72 分
3. 阿哲 95 分
4. 雅婷 60 分
5. 子豪 78 分
6. 欣怡 83 分
7. 建宏 91 分
人數:7 人
總分:567 分
平均:81.0 分
最高分:95 分(阿哲)
最低分:60 分(雅婷)
及格 6 人:['小明', '小華', '阿哲', '子豪', '欣怡', '建宏']
不及格 1 人:['雅婷']
前三高分:[95, 91, 88]其中兩行值得多看一眼。
names[scores.index(max(scores))] 是三層包起來的:max(scores) 找出最高分 95,scores.index(95) 問「95 在第幾個位置」得到 2,再拿這個 2 去 names[2] 取出「阿哲」。這是用兩個平行串列的標準手法——同一個索引在兩邊指的是同一個人,所以位置就是它們之間的橋。
sorted(scores, reverse=True)[:3] 則是先由大到小排好、再切前三個。排序回傳新串列,切片又回傳新串列,兩個操作可以直接串在一起寫成一行,scores 本身依然維持原本的順序。
至於 f'{name:<4}' 和 f'{scores[i-1]:>3}',是 f-string 的對齊語法:<4 是靠左對齊、寬度 4,>3 是靠右對齊、寬度 3,這樣印出來每一欄才會對齊成表格。詳細用法可以看字串格式化那篇。
小結
七個變數換成一個 list,程式從「加一個人就要改三個地方」變成「加一個人只要改資料本身」。這篇練到的東西整理成一句話:
- 取單筆用索引(從 0 開始、
[-1]是最後一筆),取一段用切片(終點不含) - 加用
append/insert,刪用pop(認位置、會回傳)或remove(認值、只刪第一個) - 走訪用
for,需要編號時加enumerate(..., start=1),但取資料記得[i-1] - 統計靠
len/sum/max/min,排序看你要不要保留原順序來選sorted()或sort() b = a不是複製,要複製請用a.copy()
下一步很自然就是 dict。list 用位置找資料(「第 3 個是誰」),dict 用名稱找資料(「阿哲幾分」),兩者的分工大概就是這個差別。想接著練的話可以看用 Python 字典做一個商品管理程式那篇,會發現兩種資料結構的增刪改查思路其實很像。
這些內容在「Python 基礎入門」課程裡有更完整的章節與練習題,需要從變數、流程控制一路打底的話可以從那裡開始。