回文章列表
Python10 分鐘閱讀2026年5月31日

Python 串列 list 入門:從索引、切片到一支成績統計程式

#Python#串列#list#切片#新手

文章目錄

課堂上常看到這種寫法:要記錄七位同學的期中考成績,於是開了七個變數。

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 基礎入門」課程裡有更完整的章節與練習題,需要從變數、流程控制一路打底的話可以從那裡開始。

相關課程

Python 基礎入門

從零開始學習 Python 程式語言,掌握變數、流程控制、函式與模組化設計,建立扎實的程式設計基礎。

查看課程內容