
在掌握了變數與資料型別後,下一步就是讓程式具備「思考」的能力。控制結構 (Control Structures) 是程式的靈魂,它決定了程式碼執行的順序與邏輯。
本章將帶您深入 Python 的流程控制世界,從基礎的條件判斷到 Python 3.10 引入的強大 match-case 模式比對,再到迴圈的高級控制技巧與迭代器原理。我們不僅教您「怎麼寫」,更教您「為什麼這樣寫」。
1. 邏輯決策:讓程式學會選擇
程式的核心在於根據不同情況執行不同操作。Python 使用縮排 (Indentation) 來定義程式碼區塊,這強迫了程式碼的可讀性。
1.1 條件判斷 (if-elif-else)
這是最基礎的決策結構。
age = 20
status = "student"
if age >= 18 and status == "student":
print("享有學生票優惠")
elif age >= 65:
print("享有敬老票優惠")
else:
print("請購買全票")
巢狀條件與扁平化 (Flat is better than nested)
過度巢狀的 if 會導致「波動拳」程式碼 (Hadouken Code),難以閱讀。
👎 巢狀寫法 (Bad):
if user.is_active:
if user.has_permission:
if resource.is_available:
access_resource()
👍 扁平寫法 (Good - Guard Clause):
if not user.is_active:
return
if not user.has_permission:
return
if not resource.is_available:
return
access_resource()
1.2 結構化模式比對 (Structural Pattern Matching)
Python 3.10 引入了 match-case,這不只是 switch-case 的語法糖,而是強大的解構工具。
# 傳統寫法可能是好幾個 if-elif
http_status = 404
match http_status:
case 200:
print("請求成功")
case 400 | 404: # 多重條件
print("客戶端錯誤")
case 500 | 501:
print("伺服器錯誤")
case _: # 預設情況 (Wildcard)
print("未知狀態碼")
進階解構比對
match-case 可以直接拆解數據結構,這是 if 難以做到的。
command = ["quit"]
match command:
case ["quit"]:
print("程式結束")
case ["load", filename]: # 自動將第二個元素綁定到 filename
print(f"正在讀取檔案: {filename}")
case ["save", filename, *options]: # 支援解包 (Unpacking)
print(f"儲存 {filename},選項: {options}")
case _:
print("無效指令")
2. 迴圈控制:重複的力量
迴圈讓電腦發揮其最擅長的優勢:不知疲倦地重複執行。
2.1 For 迴圈與可迭代物件 (Iterables)
for 迴圈在 Python 中是透過「迭代器協定 (Iterator Protocol)」運作的,這意味著它可以遍歷任何「可迭代」的物件(如列表、字串、字典、檔案)。
# 遍歷字典
user_scores = {"Alice": 90, "Bob": 85, "Charlie": 95}
for name, score in user_scores.items():
print(f"{name}: {score}")
Range 函數的奧秘
range() 產生的不是列表,而是一個惰性 (Lazy) 的可迭代序列,這在處理大範圍數字時極其節省記憶體。
# 記憶體效率高,不會真的產生 0 到 100萬 的列表
for i in range(0, 1000000, 2):
if i > 5:
break
print(i)
2.2 While 迴圈
當我們不知道確切的迭代次數,只知道停止條件時,使用 while。
import random
target = 5
guess = 0
while guess != target:
guess = random.randint(1, 10)
print(f"猜測: {guess}")
print("猜中了!")
2.3 迴圈控制指令
| 指令 | 描述 | 場景 |
|---|---|---|
| break | 強制跳出當前迴圈 | 找到目標後立即停止 |
| continue | 跳過本次迭代,直接進入下一次 | 過濾無效數據 |
| pass | 不做任何事 (佔位符) | 尚未實作的程式碼區塊 |
| else | 當迴圈正常結束 (未被 break) 時執行 | 搜尋失敗後的處理 |
鮮為人知的 for-else 語法
這是 Python 特有的語法,常用於搜尋場景。
numbers = [1, 3, 5, 7]
for num in numbers:
if num % 2 == 0:
print("找到偶數!")
break
else:
# 只有當迴圈完整跑完且沒有執行 break 時才會執行
print("沒有找到任何偶數")
3. 深入理解:迭代器 (Iterator) 與生成器 (Generator)
理解迭代器是晉升 Python 高手的關鍵。
可迭代 (Iterable) vs 迭代器 (Iterator)
- Iterable: 具有
__iter__方法的物件(如 list, str)。可以被for迴圈使用。 - Iterator: 具有
__next__方法的物件。負責記住目前的位置並產生下一個值。
生成器 (Generator)
生成器是一種簡易的迭代器寫法,使用 yield 關鍵字。它允許我們撰寫記憶體效率極高的大量數據處理程式。
def fibonacci(n):
a, b = 0, 1
for _ in range(n):
yield a # 暫停執行並回傳值
a, b = b, a + b
# 使用生成器
for num in fibonacci(10):
print(num, end=" ")
4. 最佳實踐:Pythonic 的流程控制
4.1 使用 enumerate 取代 range(len())
當需要同時取得索引和值時:
👎 不推薦:
fruits = ["apple", "banana"]
for i in range(len(fruits)):
print(i, fruits[i])
👍 推薦:
for i, fruit in enumerate(fruits):
print(i, fruit)
4.2 列表推導式 (List Comprehension)
對於簡單的轉換邏輯,列表推導式比 for 迴圈更簡潔且通常更快。
# 傳統寫法
squares = []
for x in range(10):
if x % 2 == 0:
squares.append(x**2)
# 推導式寫法
squares = [x**2 for x in range(10) if x % 2 == 0]
4.3 賦值表達式 (Walrus Operator :=)
Python 3.8+ 引入,能在表達式中同時賦值與回傳。
# 讀取檔案直到空行為止
while (line := file.readline()) != "":
print(line.strip())
5. 常見問題 (FAQ)
Q1: switch 語句在哪裡?
A: Python 在 3.10 之前沒有 switch,通常使用 if-elif-else 或字典映射 (dict.get) 來取代。3.10 之後請使用強大的 match-case。
Q2: 為什麼修改列表時不建議使用 for 迴圈?
A: 在迭代列表時同時修改它(如 remove 元素)會導致索引錯亂,產生預期外的結果。建議迭代列表的副本 (for item in list[:]) 或使用列表推導式建立新列表。
Q3: break 可以跳出多層巢狀迴圈嗎?
A: 不能。break 只能跳出最近的一層迴圈。若需跳出多層,建議將巢狀迴圈封裝成函數並使用 return,或使用例外處理機制。
6. 總結
流程控制是賦予程式邏輯的核心。掌握好條件判斷與迴圈,您就能撰寫出處理複雜邏輯的程式。
本章重點回顧:
- Flatten logic: 優先使用 Guard Clauses 減少巢狀
if。 - Match-Case: 善用 Python 3.10+ 的模式比對處理複雜條件。
- Iterator Protocol: 理解
for迴圈背後的迭代器機制。 - Pythonic Style: 使用
enumerate,zip, list comprehension 讓程式碼更優雅。
在下一章中,我們將學習 函數 (Functions),這是模組化程式設計的基石,讓我們能將重複的邏輯封裝並重複利用!
延伸閱讀: