Python
Python 資料結構教學:List、Dict、Set、Tuple 一次搞懂
Python 資料結構教學:List、Dict、Set、Tuple 一次搞懂
如果你曾經看著 AI 生成的程式碼裡一堆中括號和大括號發呆,這篇就是為你寫的。AI 回傳的資料幾乎都是結構化資料:API 回應通常是一個 dict,批次結果通常是一個 list。看不懂這些容器裝了什麼,你就只能照抄 AI 給的程式碼,卻抓不出它哪裡錯。
這篇會用範例帶你搞懂 Python 四種最常用的資料結構:list、dict、set、tuple,還有 AI 最愛寫也最容易寫錯的巢狀結構、推導式,以及兩個連資深工程師都會踩的陷阱。看完之後,你會多一個判斷 AI 給的程式碼對不對的能力。
你將學到什麼
四種容器一次分清楚
list、dict、set、tuple 誰有序、誰能重複、誰能修改,一個口訣記住。
list 與 dict 的核心操作
增刪查改怎麼寫,AI 最常漏掉的錯誤處理是哪一種。
set 去重與 tuple 固定值
集合運算怎麼用,函式回傳多個值為什麼會變成 tuple。
巢狀結構與推導式
看懂 list of dict,一行推導式的讀法怎麼拆解。
AI 最容易寫錯的兩個陷阱
淺複製污染原始資料、可變預設參數悄悄累積,怎麼抓出來。
三道遞進題自我檢測
讀懂、改寫、抓錯,練完就知道自己是不是真的學會了。
② 可以重複嗎?(list、tuple 可以;set 不行;dict 的 key 不行)
③ 可以修改嗎?(list、dict、set 可以;tuple 不行)
④ 怎麼取值?(list、tuple 用索引;dict 用 key;set 不能直接取)
串列 list:有序、可重複、可修改
list 是 Python 最常用的資料結構,用方括號 [] 包住,可以裝任何型別。AI 產出的程式九成以上都有 list,你要能讀懂它的增刪查改四個動作。
fruits = ["apple", "banana", "strawberry"]
# 取值(索引從 0 開始)
fruits[0] # "apple"
fruits[-1] # "strawberry"(最後一個)
# 新增
fruits.append("mango") # 加到最後
fruits.insert(1, "grape") # 插入指定位置
# 刪除
fruits.remove("banana") # 刪除第一個符合的值
fruits.pop() # 刪除並回傳最後一個
fruits.pop(0) # 刪除並回傳第 0 個
# 查詢
"apple" in fruits # True
fruits.index("strawberry") # 2(回傳索引)
len(fruits) # 元素數量
.append() 是加到最後; .insert(i, x) 是插入到第 i 個位置。 .remove(x) 找不到 x 會拋出 ValueError ,AI 常忘記加錯誤處理。排序與切片:sort 跟 sorted 不一樣
nums = [3, 1, 4, 1, 5, 9]
nums.sort() # 原地排序(改變自身):[1,1,3,4,5,9]
sorted(nums) # 回傳新串列(不改自身)
nums.sort(reverse=True) # 反向排序
nums.reverse() # 反轉串列
# 切片(回傳新 list,不改原始)
nums[1:4] # 取索引 1~3
nums[::2] # 每隔一個取一個
nums[::-1] # 反轉(同 reverse 但不修改原串列)
# 複製 list(重要!)
a = [1, 2, 3]
b = a # b 和 a 指向同一個 list!
c = a.copy() # c 是獨立的複本
c = a[:] # 同上,切片複製
b = a 不是複製,是讓 b 和 a 共用同一份資料,改 b 就會改到 a。a = [1, 2, 3]
b = a # b 和 a 共用同一個 list
b.append(4)
print(a) # [1, 2, 3, 4](a 也被改了!)
.sort() 直接改原 list,沒有回傳值(回傳 None)。 sorted(x) 回傳新 list,原 list 不變。AI 有時候寫 result = my_list.sort() ,拿到的是 None 而不是排好的串列。字典 dict:key-value 配對
dict 用 {} 包住,每個元素是「key:value」配對。API 回應幾乎都是 dict 或 dict 的 list,這是最常接觸的結構。
user = {"name": "Joan", "age": 28, "city": "Taipei"}
# 取值
user["name"] # "Joan"(key 不存在會拋 KeyError)
user.get("email") # None(key 不存在回傳 None,不報錯)
user.get("email", "N/A") # "N/A"(指定預設值)
# 新增 / 修改
user["email"] = "j@example.com" # 新增 key
user["age"] = 29 # 修改現有 key
# 刪除
del user["city"] # 刪除指定 key
user.pop("age") # 刪除並回傳值
# 遍歷
for key in user: # 遍歷所有 key
print(key, user[key])
for k, v in user.items(): # 同時拿 key 和 value
print(f"{k}: {v}")
dict[key] 取值,key 不存在就 KeyError 崩潰。AI 常忘記這件事。看到 AI 寫 data["field"] ,問它:「這個 key 一定存在嗎?如果不存在,請改用 .get() 並指定預設值。」dict 進階:合併、更新與推導式
d1 = {"a": 1, "b": 2}
d2 = {"b": 99, "c": 3}
# 合併(Python 3.9+)
merged = d1 | d2 # {"a":1, "b":99, "c":3}(d2 的 b 蓋掉 d1)
# 更新(就地)
d1.update(d2) # d1 被更新,不回傳新 dict
# 取出所有 key / value / 配對
d1.keys() # dict_keys(["a", "b"])
d1.values() # dict_values([1, 2])
d1.items() # dict_items([("a",1), ("b",2)])
# 判斷 key 存在
"a" in d1 # True(只查 key,不查 value)
1 in d1.values() # True(查 value 要用 values 方法)
names = ["Alice", "Bob", "Carol"]
# 建立 {名字:名字長度} 的字典
name_len = {name: len(name) for name in names}
# {"Alice": 5, "Bob": 3, "Carol": 5}
"Joan" in d 查的是 key,不是 value,就算 Joan 是某個 value 也會回傳 False。d = {"name": "Joan", "age": 28}
print("Joan" in d) # False!in 查的是 key,不是 value
print("name" in d) # True
集合 set 與元組 tuple
set:無序、不重複,用來去重跟做集合運算
tags = {"Python", "AI", "Python"} # 自動去重
print(tags) # {"Python", "AI"}(順序不固定)
# 新增 / 刪除
tags.add("Vibe")
tags.discard("AI") # 不存在也不報錯(remove 會報錯)
# 集合運算
a = {1, 2, 3}
b = {2, 3, 4}
a & b # 交集:{2, 3}
a | b # 聯集:{1, 2, 3, 4}
a - b # 差集:{1}(在 a 但不在 b)
# 最常用:去除 list 重複值
names = ["Alice", "Bob", "Alice"]
unique = list(set(names)) # ["Alice", "Bob"](順序不保證)
tuple:有序、不可修改,用來裝固定資料
point = (10, 20) # 座標,不應被修改
x, y = point # 解包(unpacking)
# 函式回傳多個值時,Python 自動打包成 tuple
def min_max(lst):
return min(lst), max(lst) # 回傳 tuple
lo, hi = min_max([3, 1, 4]) # 解包接收
list :有序可改,用來存一批資料; dict :key-value,用來查找; set :不重複,用來去重和比較; tuple :不可改,用來裝固定值(如座標)。巢狀結構與推導式
AI 回傳的 API 資料通常是「list 裡面裝 dict」的巢狀結構。推導式則讓你用一行取代四行迴圈,是 AI 最愛寫的簡潔語法。
巢狀結構:list of dict
students = [
{"name": "Alice", "score": 92},
{"name": "Bob", "score": 78},
{"name": "Carol", "score": 85},
]
# 取出第一個學生的名字
students[0]["name"] # "Alice"
# 遍歷所有學生
for s in students:
print(f"{s['name']}:{s['score']}")
串列推導式:一行取代 for 迴圈
nums = [1, 2, 3, 4, 5]
# 傳統寫法(五行)
squares = []
for n in nums:
squares.append(n ** 2)
# 推導式(一行)
squares = [n ** 2 for n in nums] # [1, 4, 9, 16, 25]
evens = [n for n in nums if n % 2 == 0] # [2, 4](加條件)
# 取出所有學生名字(從 list of dict)
names = [s["name"] for s in students] # ["Alice","Bob","Carol"]
[... for ... in ...] 就是推導式,等同於一個 for 迴圈加 append。讀法:「對 xxx 裡的每個元素,產生 yyy」。加了 if 就是「只取滿足條件的」。AI 最容易踩的兩個資料結構陷阱
陷阱一:直接用 = 複製 list 或 dict(淺複製問題)
original = [1, 2, 3]
copy = original # 同一個 list!
copy.append(4)
print(original) # [1, 2, 3, 4](原始也被改了)
[:] 也是同樣效果。copy = original.copy() # 或 copy = list(original)
copy = original[:] # 切片複製也可以
陷阱二:函式預設參數用 list 或 dict(可變預設參數)
items=[] 只在函式定義時建立一次,之後每次呼叫都共用同一個 list。def add_item(item, items=[]): # [] 只建立一次!
items.append(item)
return items
print(add_item("a")) # ["a"]
print(add_item("b")) # ["a", "b"](不是 ["b"]!)
def add_item(item, items=None):
if items is None:
items = []
items.append(item)
return items
三道遞進題:讀懂、改寫、抓錯
- 讀懂:API 回傳巢狀結構時,你要能逐行追蹤資料流向
- 改寫:AI 常用暴力 for 迴圈,你要能請它改用推導式或 dict
- 抓錯:淺複製和 KeyError 是最隱性的 bug,必須看得出來
題目一 讀懂(基礎)
逐行說明以下程式碼在做什麼:說明 passed 和 result 分別是什麼結構、存了什麼值,並寫出 print 的輸出結果。
students = [
{"name": "Alice", "score": 92},
{"name": "Bob", "score": 58},
{"name": "Carol", "score": 75},
]
passed = [s["name"] for s in students if s["score"] >= 60]
result = {s["name"]: s["score"] for s in students}
print(passed)
print(result)
-
passed是串列推導式,從 students 取出 score 大於等於 60 的學生名字,結果是["Alice", "Carol"](Bob 58 分被過濾)。 -
result是字典推導式,把所有學生的名字當 key、分數當 value,結果是{"Alice": 92, "Bob": 58, "Carol": 75}。 - 輸出:
["Alice", "Carol"]和{"Alice": 92, "Bob": 58, "Carol": 75}。
題目二 改寫(進階)
真實資料常有重複,AI 給的初版程式常用暴力 for 迴圈去重。懂得用 set 和 dict 去重,才能讓程式更快、更可靠,也能精準告訴 AI 該用哪種方式改寫。
# 原版:用 for 迴圈去重,效能較差
tags = ["Python", "AI", "Python", "Data", "AI"]
unique = []
for tag in tags:
if tag not in unique:
unique.append(tag)
任務:用以下提示詞請 AI 改寫,並說明改寫後的優缺點。
「請用三種方式改寫這段去重程式:
1. 用 set 去重(最快,但不保留順序)
2. 用 dict.fromkeys() 去重(保留順序)
3. 用 list 推導式加 seen set 去重(保留順序,最易讀)
每種方式加中文說明優缺點。」
- 用
list(set(tags))最快,但 set 無序,去重後順序不保證。 - 用
list(dict.fromkeys(tags))一樣快,且保留第一次出現的順序,是 Python 3.7 之後保留順序去重最簡潔的寫法。 - 推導式版:搭配一個
seen集合邊走訪邊記錄,保留順序且可讀,適合需要搭配其他條件的情況。
題目三 抓錯(高階)
資料結構的錯誤很少直接崩潰,更常是悄悄產出錯誤結果:複製問題污染原始資料、KeyError 在邊界才爆出,是最難 debug 的一種。
def process_students(students, results={}):
for s in students:
name = s["name"]
score = s["score"]
results[name] = score
return results
batch1 = [{"name": "Alice", "score": 92}]
batch2 = [{"name": "Bob", "score": 78}]
r1 = process_students(batch1)
r2 = process_students(batch2)
print(r2) # 你預期是什麼?
任務:找出兩個問題,說明 print(r2) 的實際輸出,並寫出修正提示詞。
results={} 只在函式定義時建立一次,之後每次呼叫共用同一個 dict。 r1 後 results 已有 Alice, r2 後再加 Bob,所以 print(r2) 實際是 {"Alice": 92, "Bob": 78} ,不是預期的 {"Bob": 78} 。"score" key,程式會拋出 KeyError 崩潰,應改用 s.get("score", 0) 提供預設值。results={} 改成 results=None ,函式內判斷 if results is None: results = {} ;二、 s["score"] 改用 s.get("score", 0) 防 KeyError。修正後加中文註解。」術語速查表
| 術語 | 說明 |
|---|---|
list | 有序、可重複、可修改的序列,用 [] 建立。 |
.append(x) | 將 x 加到 list 末尾; .insert(i, x) 插入到第 i 個位置。 |
.sort() 對比 sorted() | .sort() 就地排序回傳 None; sorted() 回傳新 list。 |
.copy() | 建立 list 的淺複本;直接用 = 是共用同一份資料。 |
dict | key-value 配對,key 不可重複,用 {} 或 dict() 建立。 |
.get(key, default) | 安全取 dict 值;key 不存在時回傳 default,不拋 KeyError。 |
.items() | 回傳所有 key-value 配對,常搭配 for k, v in d.items() 。 |
set | 無序、不重複,用 {值} 建立(空 set 要用 set() ,不能用 {} )。 |
.discard(x) | 從 set 刪除 x;x 不存在時不報錯( .remove(x) 會報錯)。 |
tuple | 有序、不可修改的序列,用 () 建立;常用於函式回傳多個值。 |
| 串列推導式 | [運算式 for 變數 in 序列 if 條件] ,一行取代 for 加 append。 |
| 字典推導式 | {key: value for 變數 in 序列} ,一行建字典。 |
dict.fromkeys() | 快速建字典或去重(保留順序): list(dict.fromkeys(lst)) 。 |
| 淺複製 | 只複製最外層;巢狀結構內部仍共用。 .copy() 和 [:] 都是淺複製。 |
| 可變預設參數陷阱 | 函式預設參數不能用 list 或 dict,因為只建立一次,多次呼叫共用。 |
重點整理與檢查清單
| 需求 | 用哪個 | 理由 |
|---|---|---|
| 有序、可重複、可修改 | list | 最通用的序列 |
| 用名字查找值 | dict | 查找速度快 |
| 去重、集合運算 | set | 自動去重、成員檢查快 |
| 固定不可改的資料 | tuple | 防止意外修改 |
| API 回應、資料庫結果 | list of dict | 最常見的巢狀格式 |
模組三完成清單
- 能說明 list、dict、set、tuple 的差異和各自用途
- 知道
.sort()和sorted()的差別 - 知道
=複製 list 是共用,要用.copy() - 能用
.get()安全取 dict 值,避免 KeyError - 看懂串列推導式和字典推導式
- 完成作業題目一:說明推導式的結構和輸出
- 完成作業題目二:用三種方式去重並說明優缺點
- 完成作業題目三:找出可變預設參數和 KeyError 問題
延伸學習
ChatGPT 很強,但真正讓你下班的是 Google
六小時完整實錄。從「AI 很厲害,為什麼你還是每天加班」這個問題出發,把 Google Workspace 當成真正的工作平台重新設計一次流程 ── Sheets 的資料結構、Drive 與 Docs 的文件流、Gmail 與 Calendar 的通知系統,再用 Apps Script 讓它自己跑起來,最後收斂成一張屬於你自己的 AI 工作能力地圖。
NT$ 4,599
HE201|Harness Engineering System Design(6 小時)
六小時的實作課:從 Blueprint 走到可以跑的規格,再用 No-code、n8n 低程式碼與程式碼三條路各做一次同一個 harness,最後處理可靠度——重試、錯誤處理、人工覆核。7 章 54 課,含常見坑與排錯、Capstone 實作,附學員講義 PDF。
NT$ 5,999

