💡 先搞懂問題
前面幾課的程式都有一個共同點:由上往下,每一行都會執行。可是真正要解決的問題,常常得「看情況」。虛構的「青松物流」要在網站上算運費:包裹 20 公斤以上算大件、加收 100 元;5 公斤以上算中件、加收 40 元;更輕的算小件、不加錢。如果三種算法的程式都照順序跑一遍,一件包裹會被加三次錢,這顯然不對。我們需要一種寫法,讓電腦先問一個是非題,再依答案只做其中一段。
第一次讀這種程式的人,常卡在三個地方。第一,看到 weight >= 20 不知道它是什麼:它不是在「設定」重量,而是在問「重量大於或等於 20 嗎?」,而且電腦會真的算出一個答案。第二,以為 if、elif、else 底下的每一段都會輪流執行一次。第三,搞不清楚哪幾行算是 if 的「裡面」,哪幾行不管怎樣都會執行。
這一課要學的叫條件判斷(conditional statement)。>=、== 這類符號叫比較運算子(comparison operator),它們算出來的答案只有兩種:True(成立)或 False(不成立),這兩個值的型別叫布林值(Boolean,Python 寫成 bool)。在第 2 課的記憶體模型裡,布林值和整數、字串一樣,是右欄的一個物件,名字可以貼在它上面。
weight >= 20 先查出 weight 貼著 12,再問 12 大於或等於 20 嗎,得到布林值 False,最後才把名字 is_heavy 貼上去。逐行執行播放器裡的 True、False 都畫成橘色方塊。生活比喻:物流倉庫的分揀輸送帶
青松物流的倉庫裡,包裹放上輸送帶之後,會依序經過幾道分揀閘口。第一道閘口的感應器問:「這件 20 公斤以上嗎?」答「是」,擋板一推,包裹滑進大件區,這件包裹的旅程就結束了;答「否」,包裹繼續往前。第二道閘口問:「5 公斤以上嗎?」同樣,「是」就推進中件區。輸送帶的盡頭沒有感應器,前面都沒被推走的包裹,全部掉進小件區。
所以一件 12 公斤的包裹,第一道閘口放行、第二道閘口把它推走,它根本碰不到盡頭的小件區。每件包裹最後只會落在一個區,而且「先經過哪一道閘口」很重要:如果有人把「5 公斤以上」的閘口裝在最前面,25 公斤的大包裹也會在那裡被推進中件區。
if 是第一道閘口,elif(else if 的縮寫)是後面的閘口,可以有很多道;else 是輸送帶的盡頭,不需要條件。感應器回答的「是/否」就是 True/False。包裹被推進某一區就離開輸送帶,對應的是:第一個成立的條件,它底下的那一段程式執行完,整組 if 就結束,電腦直接跳到這一組後面、沒有縮排的那一行。逐行執行播放器裡,你會看到綠色箭頭「跳過」好幾行。
這個比喻有三個地方和程式不同。第一,倉庫裡每道閘口的感應器都一直開著;Python 則是「輪到才問」,前面的條件已經成立,後面的條件連算都不會算。第二,倉庫的閘口位置是固定的,程式裡「哪幾行屬於哪一道閘口」卻是靠縮排(indentation,行首空白)決定,少縮或多縮一格,意思就完全不同,原理補完會畫給你看。第三,輸送帶會一件接一件處理包裹,這一課的程式一次只判斷一件;要讓同一段判斷處理很多筆資料,得等第 8 課的迴圈。
🎮 互動實驗室一:跟著綠色箭頭走一趟運費計算
這是一段真的用 Python 執行過、逐行記錄下來的運費程式。右上角可以切換包裹重量(25、12、3 公斤),三種重量會走三條不同的路。按「下一步」執行一行;綠色箭頭 ➜ 指的是「下一個要執行的行」,黃色 ▸ 是「剛剛執行完的行」。遇到 🤔 問題時,先猜綠色箭頭會跳到第幾行,再按下一步對答案。
if is_heavy: 執行完的那一步:條件是 False 時,綠色箭頭會直接跳過第 6、7 行;二是某個區塊的最後一行執行完的那一步:箭頭會越過同一組剩下的 elif、else,落在第 13 行。另外,第 11 行的 else: 從頭到尾都不會被綠色箭頭指到,因為它本身沒有要做的事,選 3 公斤時箭頭會從第 8 行直接跳到第 12 行。右邊記憶體裡,is_heavy 貼著的是橘色的布林值方塊,不是字串。🎮 互動實驗室二:同一個 95 分,三種寫法三種結果
青松補習班要把模擬考成績分成 A(90 分以上)、B(75 分以上)、C(60 分以上)、D 四級,拿到 A 而且缺席不超過 3 次的人可以領獎學金。右上角的選單有三種寫法,分數都是 95、缺席都是 2 次。先選 ①,看正確的寫法怎麼走;再切到 ② 和 ③,看條件排錯順序、或把 elif 改成好幾個 if 時,箭頭和 level 的標籤會怎麼變。
score >= 60 放在最前面,95 分在第一道關卡就被攔成 C,後面的 elif 連看都沒看,獎學金也跟著沒了。③ 的三個 if 彼此獨立,每一個都會重新檢查,所以 level 這張標籤被撕下來改貼了兩次(A → B → C),最後留下的是最後一次貼的 C。請在 ③ 裡盯著右邊記憶體:每次改貼,舊的字串方塊都會淡出。🎮 互動實驗室三:預測輸出卡
每張卡是一小段程式,請先在心裡跑一遍,再選出它會印出什麼。選完會顯示對錯、真正的執行結果和原因。所有輸出都是用 Python 3.13 實際執行得到的。卡片涵蓋比較運算、= 與 ==、大小寫、and/or/not、真假值、縮排、elif 順序與巢狀 if。
📘 原理補完
1. 六個比較運算子:每一個都是在問是非題
Python 官方文件把 <、>、==、>=、<=、!= 叫做比較運算子,它們的結果一律是布林值 True 或 False。注意兩個字母大寫開頭:寫成 true 會得到 NameError,因為 Python 把它當成一個還沒建立的名字。下表的結果都是實際執行得到的。
| 運算子 | 讀法 | 例子 | 結果 | 新手容易誤會的地方 |
|---|---|---|---|---|
== | 等於嗎? | 7 == 7.0 | True | 兩個等號才是「比較」;一個等號 = 是第 2 課的「貼標籤」 |
!= | 不等於嗎? | 10 != 10 | False | 驚嘆號在前、等號在後,中間不能有空白 |
>、< | 大於嗎?小於嗎? | 7 > 3 | True | 剛好相等時是 False:5 > 5 不成立 |
>=、<= | 大於或等於嗎? | 20 >= 20 | True | 等號一定寫在後面;=> 是語法錯誤 |
==(字串) | 一模一樣嗎? | "Yes" == "yes" | False | 大小寫不同就是不同字串;空白也算字元 |
==(不同型別) | 等於嗎? | 5 == "5" | False | 整數 5 和字串 "5" 不相等;用 > 比較它們則會直接報 TypeError |
比較可以連著寫:0 <= 7 < 10 的意思是「7 大於或等於 0,而且 7 小於 10」,結果是 True。這是 Python 特有的方便寫法,看到時把它拆成兩個比較、中間用「而且」接起來就好。
2. if、elif、else 的規則:由上往下,只走一條路
一組條件判斷由一個 if 開頭,後面可以接任意多個 elif,最後可以有一個 else(elif 和 else 都可以省略)。每一行條件都以冒號 : 結尾,底下縮排的那幾行叫做這個條件的區塊(block,官方文件稱為 suite)。電腦執行的步驟是:
- 算出 if 後面的條件。如果是 True,執行它底下的區塊,做完就離開整組判斷,跳到這一組後面第一行沒有縮排的程式。
- 如果是 False,跳過那個區塊,去算下一個 elif 的條件,用同樣的方式處理。
- 所有條件都是 False 時,如果有 else,就執行 else 的區塊;沒有 else,就什麼都不做。
所以一組 if/elif/else 裡最多只有一個區塊會被執行。這也表示:寫到某個 elif 時,你可以確定「它上面的條件全都不成立」。分數分級寫成「由嚴到寬」就是在利用這一點:
score = 72
if score >= 90: # 由嚴到寬:門檻最高的放最前面
level = "A"
elif score >= 75: # 能走到這裡,代表 score 一定小於 90
level = "B"
elif score >= 60: # 也一定小於 75,所以不必寫成 60 <= score < 75
level = "C"
else: # 前面全部不成立:小於 60
level = "D"
print(level) # 印出 C
實驗室二的 ③ 示範了另一種常見寫法:把 elif 全部寫成 if。下表整理幾種寫法的差別,判斷一段程式「會執行幾個區塊」時,先看它們是不是同一組。
| 寫法 | 會檢查幾個條件 | 最多執行幾個區塊 | 適合的情況 |
|---|---|---|---|
if 單獨一個 | 1 個 | 0 或 1 個 | 「有需要才多做一件事」,例如離島加收運費 |
if … else | 1 個 | 剛好 1 個 | 二選一,例如及格/不及格 |
if … elif … else | 從上往下,遇到第一個 True 就停 | 剛好 1 個 | 多選一的分級,例如 A/B/C/D,條件順序很重要 |
if … elif(沒有 else) | 從上往下,遇到第一個 True 就停 | 0 或 1 個 | 多選一,但「都不符合」時什麼都不做 |
連續好幾個獨立的 if | 每一個都會檢查 | 可能好幾個 | 彼此無關的檢查,例如表單上「暱稱空白」「密碼太短」要同時提醒 |
3. 縮排決定哪些行屬於 if
很多程式語言用大括號 { } 把區塊包起來,Python 只看縮排:冒號下一行開始、比 if 多縮進去的連續幾行,就是它的區塊;第一行「縮回來」的程式,代表區塊結束了。官方風格指南 PEP 8 建議每一層縮排用 4 個空白,大多數編輯器按 Tab 鍵時會自動插入空白。真正的規則是:同一個區塊裡每一行的縮排必須一樣多。
18 > 25 不成立時,整個黃色區塊都會被跳過。4. and、or、not:把好幾個條件組起來
要同時看兩個條件時,用邏輯運算子(logical operator)。A and B 是「A 而且 B」,兩個都成立才成立;A or B 是「A 或者 B」,有一個成立就成立;not A 把真假翻過來。它們都用英文單字寫,不是符號:寫成 && 或 ||(其他語言的寫法)在 Python 裡是語法錯誤。
還有一個實用的細節:Python 由左往右算,一旦答案已經確定就停下來,叫做短路求值(short-circuit evaluation)。A and B 裡 A 已經是 False,整體不可能成立,B 就不算了;A or B 裡 A 已經是 True,B 也不算。實驗室二的 ② 裡,level == "A" 是 False,右邊的 absent <= 3 就沒有被計算。另外,and 的優先順序比 or 高,條件一長就加上括號,例如 (day == "六" or day == "日") and not raining,自己和別人讀起來都不會誤會。
5. = 與 ==、字串大小寫
一個等號 = 是指派:把名字貼到物件上,這一行不會產生 True 或 False。兩個等號 == 才是在問「相等嗎?」。在 if 後面誤寫成一個等號,Python 會在程式開始執行之前就發現,連第 1 行都不會跑,而且 3.13 的錯誤訊息會直接提示你是不是想寫 ==:
終端機畫面(Python 3.13;File 後面原本是完整的檔案路徑,這裡簡寫成 main.py) File "main.py", line 2 if x = 5: ^^^^^ SyntaxError: invalid syntax. Maybe you meant '==' or ':=' instead of '='?
字串比較是一個字元一個字元比,大寫 "Y" 和小寫 "y" 是不同的字元,所以 "Yes" == "yes" 是 False,前後多一個空白也一樣不相等。處理使用者輸入時,常見的做法是先用第 4 課學過的 .strip() 去掉前後空白、.lower() 轉成小寫,再拿去比較。另外,第 3 課說過 input() 拿到的一定是字串,直接拿字串和數字比大小會出錯,要先用 int() 轉換:
age = "20" 之後執行 if age >= 18:Traceback (most recent call last): File "main.py", line 2, in <module> if age >= 18: ^^^^^^^^^ TypeError: '>=' not supported between instances of 'str' and 'int'
6. 巢狀 if:判斷裡面再判斷
if 的區塊裡可以再放一組 if,叫做巢狀(nested)if。外層先決定「要不要進來」,進來之後內層再細分。每多一層就多縮排 4 個空白,所以讀巢狀 if 時,把同一條直線上的 if、elif、else 看成同一組。巢狀超過兩三層就很難讀,常見的整理方式是用 and 合併條件,或是先把「不處理」的情況排除掉。
total >= 1000。不是會員的人,內層那一組連看都不會看。7. 真假值:不是 True/False 的東西也能放進 if
if 後面不一定要寫比較運算。把一個數字、字串或串列直接放進去,Python 會把它當成 True 或 False 來看,這叫做真假值(truthiness,官方文件稱為 truth value testing)。規則可以濃縮成一句:「零」和「空」是假的,其他幾乎都是真的。可以用 bool(值) 直接查。
bool() 實際查過的結果。最容易看錯的是右下角:字串 "0"、"False" 和串列 [0] 看起來像「假」,但它們都有內容,所以是真值。這個規則讓「有沒有東西」的檢查變得很短:if cart: 就是「購物車裡有東西嗎?」,if not name: 就是「名字是空的嗎?」。None 是 Python 表示「沒有值」的特殊物件,例如還沒填的欄位;它在 if 裡也被當成 False。
8. 常見錯誤與錯誤訊息的長相
和條件判斷有關的錯誤分成兩種。第一種是語法錯誤(SyntaxError)與縮排錯誤(IndentationError):Python 在執行前會先把整個檔案讀過一遍,發現寫法不合規則就直接停下來,所以一行都不會執行,連錯誤那行前面的 print 也不會印。第二種是執行到一半才發生的錯誤,例如 NameError,它前面的程式已經跑過了。以下都是 Python 3.13 實際執行的畫面:
① if 那一行忘了冒號 File "main.py", line 2 if score >= 60 ^ SyntaxError: expected ':'
② if 底下的那一行沒有縮排 File "main.py", line 3 print("及格") ^^^^^ IndentationError: expected an indented block after 'if' statement on line 2
③ 沒有 if 的地方突然多縮排了 File "main.py", line 3 print("縮排") IndentationError: unexpected indent
④ 縮回來的位置對不上任何一層(這裡只縮了 2 個空白) File "main.py", line 4 print("完成") ^ IndentationError: unindent does not match any outer indentation level
⑤ 每一條路都沒有替 msg 貼標籤(hour = 23,兩個條件都不成立)Traceback (most recent call last): File "main.py", line 6, in <module> print(msg) ^^^ NameError: name 'msg' is not defined
第 ⑤ 種最隱蔽:程式平常測試都正常,只有遇到「每個條件都不成立」的資料時才出錯。修法是加上 else,或是在 if 之前先替 msg 貼一個預設值。讀錯誤訊息的方法:先看最後一行的錯誤類型和說明,再看上面的 line 幾,第 10 課會完整練習。
9. 兩段完整範例
第一段把這一課的觀念放在一起:外層 if/else 先排除超重包裹,else 裡面是巢狀的分級,再用 or 與 and 處理加收與折扣。請試著先自己追一次 fee 的變化(60 → 100 → 80),再對照最後的輸出。
# 青松物流運費(示意):先分大中小件,再看離島與會員
weight = 18 # 包裹重量(公斤)
island = False # 是否寄往離島
is_member = True # 是否為會員
if weight > 30: # 超過 30 公斤不收件
print("超重,請改用貨運")
else:
fee = 60 # 基本運費
if weight >= 20: # 巢狀 if:確定沒超重之後,才分級距
fee = fee + 100
elif weight >= 5: # 只有上一個條件不成立時才會檢查
fee = fee + 40
if island or weight >= 20: # or:任一個成立就加收 50
fee = fee + 50
if is_member and fee >= 100: # and:兩個都成立才折 20 元
fee = fee - 20
print("運費", fee) # 還在 else 區塊裡(有縮排)
# 輸出:運費 80
第二段是表單檢查,示範真假值和字串比較。三個 if 彼此獨立,因為「暱稱空白」和「沒選興趣」可能同時發生,兩個提醒都要顯示。
# 會員註冊表單的檢查(示意)
nickname = " " # 使用者只打了兩個空白
tags = [] # 一個興趣都沒勾
answer = "YES" # 同意條款嗎?使用者打了大寫
nickname = nickname.strip() # 去掉前後空白,變成空字串 ""
if not nickname: # 空字串被當成 False,not 之後變成 True
print("暱稱不能空白")
if tags: # 空串列被當成 False
print("興趣:", tags)
else:
print("還沒選興趣")
if answer.lower() == "yes": # 先轉成小寫再比,YES、Yes、yes 都算同意
print("已同意條款")
# 輸出:
# 暱稱不能空白
# 還沒選興趣
# 已同意條款
以後讀 AI 或資料分析的範例程式,if 幾乎無所不在:訓練程式開頭常有「如果有 GPU 就用 GPU,否則用 CPU」,資料清理時「如果這一格是空的就補值」,呼叫語言模型的程式裡「如果模型要求使用工具就去執行,否則直接回覆」。看到它們時,先找出冒號和縮排,就知道每一條路包含哪幾行。
✅ 自我檢測
6 題原創的程式閱讀題,每一題的答案都用 Python 3.13 實際執行確認過。選完立即顯示對錯與解析,全部作答後會出現總分。目前得分:0 / 6