Lists in Python

講師:蔡政廷

Sprout 2026

Motivation

情境——成績統計

  • 你有全班 n 位同學的成績

  • 請計算有多少人的成績大於平均

三位學生的情境

score1 = int(input("Student #1's score: "))
score2 = int(input("Student #2's score: "))
score3 = int(input("Student #3's score: "))
total_score = 0
total_score += score1
total_score += score2
total_score += score3
avg_score = total_score / 3
n_above_avgs = 0
if score1 > avg_score:
    n_above_avgs += 1
if score2 > avg_score:
    n_above_avgs += 1
if score3 > avg_score:
    n_above_avgs += 1
print(f"Average score: {avg_score}")
print(f"Number of above-average students: {n_above_avgs}")
  • 有點累,但不難對吧

  • 如果學生再多一點怎麼辦?

三十位學生的情境

score1 = int(input("Student #1's score: "))
score2 = int(input("Student #2's score: "))
score3 = int(input("Student #3's score: "))
score4 = int(input("Student #4's score: "))
score5 = int(input("Student #5's score: "))
score6 = int(input("Student #6's score: "))
score7 = int(input("Student #7's score: "))
score8 = int(input("Student #8's score: "))
score9 = int(input("Student #9's score: "))
score10 = int(input("Student #10's score: "))
score11 = int(input("Student #11's score: "))
score12 = int(input("Student #12's score: "))
score13 = int(input("Student #13's score: "))
score14 = int(input("Student #14's score: "))
score15 = int(input("Student #15's score: "))
score16 = int(input("Student #16's score: "))
score17 = int(input("Student #17's score: "))
score18 = int(input("Student #18's score: "))
score19 = int(input("Student #19's score: "))
score20 = int(input("Student #20's score: "))
score21 = int(input("Student #21's score: "))
score22 = int(input("Student #22's score: "))
score23 = int(input("Student #23's score: "))
score24 = int(input("Student #24's score: "))
score25 = int(input("Student #25's score: "))
score26 = int(input("Student #26's score: "))
score27 = int(input("Student #27's score: "))
score28 = int(input("Student #28's score: "))
score29 = int(input("Student #29's score: "))
score30 = int(input("Student #30's score: "))
total_score = 0
total_score += score1
total_score += score2
total_score += score3
total_score += score4
total_score += score5
total_score += score6
total_score += score7
total_score += score8
total_score += score9
total_score += score10
total_score += score11
total_score += score12
total_score += score13
total_score += score14
total_score += score15
total_score += score16
total_score += score17
total_score += score18
total_score += score19
total_score += score20
total_score += score21
total_score += score22
total_score += score23
total_score += score24
total_score += score25
total_score += score26
total_score += score27
total_score += score28
total_score += score29
total_score += score30
avg_score = total_score / 30
n_above_avgs = 0
if score1 > avg_score:
    n_above_avgs += 1
if score2 > avg_score:
    n_above_avgs += 1
if score3 > avg_score:
    n_above_avgs += 1
if score4 > avg_score:
    n_above_avgs += 1
if score5 > avg_score:
    n_above_avgs += 1
if score6 > avg_score:
    n_above_avgs += 1
if score7 > avg_score:
    n_above_avgs += 1
if score8 > avg_score:
    n_above_avgs += 1
if score9 > avg_score:
    n_above_avgs += 1
if score10 > avg_score:
    n_above_avgs += 1
if score11 > avg_score:
    n_above_avgs += 1
if score12 > avg_score:
    n_above_avgs += 1
if score13 > avg_score:
    n_above_avgs += 1
if score14 > avg_score:
    n_above_avgs += 1
if score15 > avg_score:
    n_above_avgs += 1
if score16 > avg_score:
    n_above_avgs += 1
if score17 > avg_score:
    n_above_avgs += 1
if score18 > avg_score:
    n_above_avgs += 1
if score19 > avg_score:
    n_above_avgs += 1
if score20 > avg_score:
    n_above_avgs += 1
if score21 > avg_score:
    n_above_avgs += 1
if score22 > avg_score:
    n_above_avgs += 1
if score23 > avg_score:
    n_above_avgs += 1
if score24 > avg_score:
    n_above_avgs += 1
if score25 > avg_score:
    n_above_avgs += 1
if score26 > avg_score:
    n_above_avgs += 1
if score27 > avg_score:
    n_above_avgs += 1
if score28 > avg_score:
    n_above_avgs += 1
if score29 > avg_score:
    n_above_avgs += 1
if score30 > avg_score:
    n_above_avgs += 1
print(f"Average score: {avg_score}")
print(f"Number of above-average students: {n_above_avgs}")
  • 行數:125 行

  • 就算你可以複製貼上,這還是太沒有效率了

缺了什麼?

  • 儲存多個值的能力

    • 不,變數要多少有多少
  • 用其他變數指定變數的能力

Introduction to Lists

List 是什麼

Lists are mutable sequences, …

——Python 官方文件

  • List 是可變的(mutable)sequence

    • 但什麼是 sequence?

Sequence 是什麼

[Sequences] represent finite ordered sets indexed by non-negative numbers.

——Python 官方文件

  • set:裝不只一個東西

  • finite:裝的東西數量有限

  • ordered:裡面的東西有先後次序

  • indexed by non-negative numbers:可以用非負整數選取元素

實際來操作吧!

  • 宣告一個 list

    scores = [67, 42, 87]
    print(scores[2])
  • 以上的程式碼會 print 出什麼呢?

List 索引

  • Python(以及多數程式語言)的類 list 的索引是從 0 開始

  • 因此:

    scores = [67, 42, 87]

    的索引—值如下對應:

    • scores[0] \(\to\) 67

    • scores[1] \(\to\) 42

    • scores[2] \(\to\) 87

用 list 來重構吧!

scores[0] = int(input("Student #1's score: "))
scores[1] = int(input("Student #2's score: "))
scores[2] = int(input("Student #3's score: "))
total_score = 0
total_score += scores[0]
total_score += scores[1]
total_score += scores[2]
avg_score = total_score / 3
n_above_avgs = 0
if scores[0] > avg_score:
    n_above_avgs += 1
if scores[1] > avg_score:
    n_above_avgs += 1
if scores[2] > avg_score:
    n_above_avgs += 1
print(f"Average score: {avg_score}")
print(f"Number of above-average students: {n_above_avgs}")
  • 好像沒差很多?

    • 我們還沒善用索引的優勢

當 list 遇上 for 迴圈

scores = [0, 0, 0]
for i in range(3):
    scores[i] = int(input(f"Student #{i+1}'s score: "))
total_score = 0
for i in range(3):
    total_score += scores[i]
avg_score = total_score / 3
n_above_avgs = 0
for i in range(3):
    if scores[i] > avg_score:
        n_above_avgs += 1
print(f"Average score: {avg_score}")
print(f"Number of above-average students: {n_above_avgs}")
  • 改成三十位學生要改幾處?

  • 程式還能再更短嗎?

深入了解 Sequence

Python 有哪些 sequence?

  • list 是可變的(mutable)sequence

  • 這代表存在不可變的(immutable)sequence

    • tuple 除了裡面的東西不可更改之外,其餘都跟 list 一樣

    • str 是只能裝文字的不可變 sequence

    • range 也是一種不可變 sequence!

如何掃過一個 sequence

  • 善用索引的能力!

    scores = [67, 42, 87]
    for i in range(len(scores)):
        print(scores[i])
  • len(x):取得 x 的長度

  • 有沒有什麼很冗的地方?

冗言贅字

  • 我們知道 range 也是 sequence,所以可以這樣寫:

    scores = range(3)
    for i in range(len(scores)):
        print(scores[i])
  • ……但我們平常不都這樣寫嗎:

    for i in range(3):
        print(i)

簡潔有力

  • 所以其實可以這樣寫:

    scores = [67, 42, 87]
    for i in scores:
        print(i)
  • 可以放在 for ... in ... 後面的東西叫做 iterable(可迭代的)

再次重構

scores = [0, 0, 0]
for i in range(3):
    scores[i] = int(input(f"Student #{i+1}'s score: "))
total_score = 0
for i in scores:
    total_score += i
avg_score = total_score / 3
n_above_avgs = 0
for i in scores:
    if i > avg_score:
        n_above_avgs += 1
print(f"Average score: {avg_score}")
print(f"Number of above-average students: {n_above_avgs}")
  • 什麼情況下還是會選擇使用 for ... in range(...) 的方式?

    • 當你有其他地方要用索引值的時候

沒有 for 的 in

  • for ... in ... 中的 in 是關鍵字

  • 如果單用 in 會怎麼樣?

  • scores = [67, 42, 87]
    print(67 in scores)     # True
    print(66 in scores)     # False
    print(66 not in scores) # True

    告訴你某元素有沒有在 sequence 中

加法?乘法?

  • + 可以用來把兩個相同型別的 sequence 接在一起:

    foo = [1, 2, 3]
    bar = [4, 5, 6]
    print(foo + bar) # [1, 2, 3, 4, 5, 6]
  • * 以一個正整數就相當於連加:

    foo = [1, 2, 3]
    print(foo * 3) # [1, 2, 3, 1, 2, 3, 1, 2, 3]

成績統計——任意人數

n_students = int(input(f"Number of students: "))
scores = [0] * n_students
for i in range(n_students):
    scores[i] = int(input(f"Student #{i+1}'s score: "))
total_score = 0
for i in scores:
    total_score += i
avg_score = total_score / n_students
n_above_avgs = 0
for i in scores:
    if i > avg_score:
        n_above_avgs += 1
print(f"Average score: {avg_score}")
print(f"Number of above-average students: {n_above_avgs}")

更多雜七雜八的 function

  • min(x):回傳 x 中的最小值

  • max(x):回傳 x 中的最大值

  • sum(x):回傳 x 中的所有元素的總和

  • sorted(x):回傳有小到大排好的 x

  • reversed(x):回傳順序顛倒的 x

四行換一行

n_students = int(input(f"Number of students: "))
scores = [0] * n_students
for i in range(n_students):
    scores[i] = int(input(f"Student #{i+1}'s score: "))
avg_score = sum(scores) / n_students
n_above_avgs = 0
for i in scores:
    if i > avg_score:
        n_above_avgs += 1
print(f"Average score: {avg_score}")
print(f"Number of above-average students: {n_above_avgs}")

實戰練習

  • 題目: 688627

  • 先用 a = list(map(int, input().split())) 把輸入讀成 list

  • 上完課後試試看能不能只用三行解上面兩題

Indexing & Slicing

負的索引值?!

  • 如果方括號裡面放負數會怎麼樣?

  • scores = [67, 42, 87]
    print(scores[-1]) # 87
    print(scores[-2]) # 42
    print(scores[-3]) # 67

    索引會變成倒過來數!

切片

  • 索引也可以用來一次選取多個元素,稱之為 slice

  • Slice 的語法為「3S」—— [start:stop:step]

    • start:從哪一格開始

    • stop:在哪一格之前結束

    • step:一次走幾格

  • foo = list(range(10))
    print(foo[3:9:2]) # [3, 5, 7]

切片簡寫

  • Slice 語法中的三個 S,全部都可以省略不寫

  • 不寫的時候會用預設值:

    • start:預設為 0

    • stop:預設為 sequence 的長度

    • step:預設為 1

常用切片

foo = list(range(10))
print(foo[3:7])     # [3, 4, 5, 6]
print(foo[:3])      # [0, 1, 2]
print(foo[:-3])     # [0, 1, 2, 3, 4, 5, 6]
print(foo[3:])      # [3, 4, 5, 6, 7, 8, 9]
print(foo[-3:])     # [7, 8, 9]
print(foo[::2])     # [0, 2, 4, 6, 8]
print(foo[1::2])    # [1, 3, 5, 7, 9]
print(foo[::-1])    # [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
print(foo[:])       # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

善變的 List

關於可變性

  • list 是可變的 sequence,代表創建之後可以修改其中的值

    foo = [67, 42, 87]
    foo[1] = 95 # OK
    bar = (67, 42, 87)
    bar[1] = 95 # TypeError: 'tuple' object does not support item assignment
  • Python 內建的可變 sequence 除了 list 就只剩 bytearray

新增元素

  • list.append(x):將 x 新增到 list 的尾端

  • list.extend(iterable):把 iterable 接到 list 後面

  • list.insert(i, x):把 x 插入到索引 i 的位置

    原本在索引 i 及之後的元素會被往後推一格

移除元素

  • list.remove(x):移除第一個等於 x 的元素

  • list.pop(i):移除並回傳位於索引 i 的元素

    如果沒有指定 i,則移除最後一個元素

  • list.clear():移除所有元素

重新排列

  • list.sort():原地由小到大排序 list

  • list.reverse():原地反轉 list

  • 此兩者與 sorted()reversed() 的差別

    在於後者僅回傳排序/反轉後的新 list,而不修改原本的 list

量子糾纏?

  • 以下的程式碼試圖複製出一份新的 list 並修改其值:

    foo = [67, 42, 87]
    bar = foo
    bar[1] = 95
    print(foo) # [67, 95, 87]
  • 修改新 list 卻也改到了舊的 list?

Python 的賦值

  • Python 中的「賦值」(=)更像是「指向」:

    foo = [67, 42, 87]  # 創建 list [67, 42, 87] 並讓 foo 指向它
    bar = foo           # 讓 bar 指向 foo 所指向的東西
    bar[1] = 95         # 修改 bar 所指向的東西
  • 解決方法是明確地創造一份原 list 的拷貝:

    foo = [67, 42, 87]  # 創建 list [67, 42, 87] 並讓 foo 指向它
    bar = foo[:]        # 從 foo 切出新的 list,並讓 bar 指向它
    bar[1] = 95         # 修改 bar 所指向的東西

慣例的重構

n_students = int(input(f"Number of students: "))
scores = []
for i in range(n_students):
    scores.append(int(input(f"Student #{i+1}'s score: ")))
avg_score = sum(scores) / n_students
n_above_avgs = 0
for i in scores:
    if i > avg_score:
        n_above_avgs += 1
print(f"Average score: {avg_score}")
print(f"Number of above-average students: {n_above_avgs}")
  • 還能更短嗎?

    • …還能再少五行

Map & Comprehension

如何讀一個 list

  • 假設我們要讀取使用者輸入的 list

    格式為空白分隔的數字,如 67 42 87

  • x = input().split() # ['67', '42', '87']
    for i in range(len(x)):
        x[i] = int(x[i])
  • 我們得把 list 中的每一個字串轉成整數

    • 有更簡潔的寫法嗎?

逐項套用

  • map(function, iterable)

    iterable 中的每一項元素套用 function,並回傳其結果

  • 因此我們可以這樣寫:

    x = list(map(int, input().split()))

List comprehension

  • map 只能對每一項直接套用某函式,使用上較沒有彈性

    (伏筆:lambda 函式可以解決這問題)

  • 這時就可以使用 list comprehension:

    [expression for item in list if condition]

Can you comprehend?

# 讀進一個整數的 list
nums = [int(x) for x in input().split()]

foo = list(range(5))
print([x**2 for x in foo])              # [0, 1, 4, 9, 16]
print([x for x in foo if x % 2 == 0])   # [0, 2, 4]
# 提示:可以靠這邊的技巧,只用三行解出 #688 和 #627 嗎?

bar = [[3*y + x + 1 for x in range(3)] for y in range(3)]
print(bar)
# [[1, 2, 3],
#  [4, 5, 6],
#  [7, 8, 9]]
print(bar[1][2]) # 6
# 提示:list 裡面包 list 就叫做 2D list,或許很適合 #684

最終的重構

n_students = int(input(f"Number of students: "))
scores = [int(input(f"Student #{i+1}'s score: ")) for i in range(n_students)]
avg_score = sum(scores) / n_students
n_above_avgs = sum([1 for x in scores if x > avg_score])
print(f"Average score: {avg_score}")
print(f"Number of above-average students: {n_above_avgs}")
  • 行數:6 行

  • 這就是 Python list 的力量

今日習題