🗺️ 程式互動式地圖
零基礎起步・第 7 課

條件判斷:讓程式做選擇

程式不是每一行都會執行。這一課看懂電腦怎麼「問一個是非題」,再依答案決定走哪一條路、跳過哪幾行。

if/elif/else 只走一條路 綠色箭頭跳過哪幾行 縮排、and/or、真假值

💡 先搞懂問題

前面幾課的程式都有一個共同點:由上往下,每一行都會執行。可是真正要解決的問題,常常得「看情況」。虛構的「青松物流」要在網站上算運費:包裹 20 公斤以上算大件、加收 100 元;5 公斤以上算中件、加收 40 元;更輕的算小件、不加錢。如果三種算法的程式都照順序跑一遍,一件包裹會被加三次錢,這顯然不對。我們需要一種寫法,讓電腦先問一個是非題,再依答案只做其中一段。

第一次讀這種程式的人,常卡在三個地方。第一,看到 weight >= 20 不知道它是什麼:它不是在「設定」重量,而是在問「重量大於或等於 20 嗎?」,而且電腦會真的算出一個答案。第二,以為 if、elif、else 底下的每一段都會輪流執行一次。第三,搞不清楚哪幾行算是 if 的「裡面」,哪幾行不管怎樣都會執行。

這一課要學的叫條件判斷(conditional statement)。>=、== 這類符號叫比較運算子(comparison operator),它們算出來的答案只有兩種:True(成立)或 False(不成立),這兩個值的型別叫布林值(Boolean,Python 寫成 bool)。在第 2 課的記憶體模型裡,布林值和整數、字串一樣,是右欄的一個物件,名字可以貼在它上面。

is_heavy = weight >= 20 weight 目前貼在 12 上 ① 先算右邊的問句 weight >= 20 12 >= 20 False 查名字 比大小 答案只有兩種: True 或 False ② 再把名字貼到答案上 名字(變數) 物件(真正的資料) 全域(主程式) weightis_heavy 整數 int 12 布林值 bool False 新貼上的標籤
比較運算和第 2 課的指派一樣「先算右邊」:weight >= 20 先查出 weight 貼著 12,再問 12 大於或等於 20 嗎,得到布林值 False,最後才把名字 is_heavy 貼上去。逐行執行播放器裡的 True、False 都畫成橘色方塊。

生活比喻:物流倉庫的分揀輸送帶

青松物流的倉庫裡,包裹放上輸送帶之後,會依序經過幾道分揀閘口。第一道閘口的感應器問:「這件 20 公斤以上嗎?」答「是」,擋板一推,包裹滑進大件區,這件包裹的旅程就結束了;答「否」,包裹繼續往前。第二道閘口問:「5 公斤以上嗎?」同樣,「是」就推進中件區。輸送帶的盡頭沒有感應器,前面都沒被推走的包裹,全部掉進小件區。

所以一件 12 公斤的包裹,第一道閘口放行、第二道閘口把它推走,它根本碰不到盡頭的小件區。每件包裹最後只會落在一個區,而且「先經過哪一道閘口」很重要:如果有人把「5 公斤以上」的閘口裝在最前面,25 公斤的大包裹也會在那裡被推進中件區。

12 kg 閘口 1 20 公斤以上? 否 → 閘口 2 5 公斤以上? 是 ↓ 盡頭 不用再問 大件區+100 元 中件區+40 元 📦 在這裡 小件區碰不到 ifweight >= 20: elifweight >= 5: else(其他全部) 每件包裹 只落在一區
12 公斤的包裹在閘口 1 得到「否」,繼續往前;在閘口 2 得到「是」,被推進中件區,旅程結束。盡頭的小件區畫成虛線,因為這件包裹永遠到不了那裡。三個區對應程式裡的 if、elif、else(數字為示意)。
回到程式:每一道閘口就是一個條件。if 是第一道閘口,elif(else if 的縮寫)是後面的閘口,可以有很多道;else 是輸送帶的盡頭,不需要條件。感應器回答的「是/否」就是 True/False。包裹被推進某一區就離開輸送帶,對應的是:第一個成立的條件,它底下的那一段程式執行完,整組 if 就結束,電腦直接跳到這一組後面、沒有縮排的那一行。逐行執行播放器裡,你會看到綠色箭頭「跳過」好幾行。
12345678910111213 weight = 12 fee = 60 is_heavy = weight >= 20 print("重貨?", is_heavy) if is_heavy: fee = fee + 100 print("大件") elif weight >= 5: fee = fee + 40 print("中件") else: print("小件") print("運費", fee) 第 5 行:False is_heavy 是 False,整段 縮排區塊(6、7 行)跳過 第 8 行:True 12 >= 5 成立,進入區塊 已經走完一條路 else(11、12 行)不必看 第 13 行沒縮排:一定執行 亮的是執行到的行,暗的是被跳過的行
同一段程式在 weight = 12 時的路線。左邊兩條綠色箭頭就是播放器裡你會看到的「跳行」:條件不成立,跳過底下縮排的整段;某一條路走完,同一組裡剩下的 elif、else 全部跳過。

這個比喻有三個地方和程式不同。第一,倉庫裡每道閘口的感應器都一直開著;Python 則是「輪到才問」,前面的條件已經成立,後面的條件連算都不會算。第二,倉庫的閘口位置是固定的,程式裡「哪幾行屬於哪一道閘口」卻是靠縮排(indentation,行首空白)決定,少縮或多縮一格,意思就完全不同,原理補完會畫給你看。第三,輸送帶會一件接一件處理包裹,這一課的程式一次只判斷一件;要讓同一段判斷處理很多筆資料,得等第 8 課的迴圈。

🎮 互動實驗室一:跟著綠色箭頭走一趟運費計算

這是一段真的用 Python 執行過、逐行記錄下來的運費程式。右上角可以切換包裹重量(25、12、3 公斤),三種重量會走三條不同的路。按「下一步」執行一行;綠色箭頭 ➜ 指的是「下一個要執行的行」,黃色 ▸ 是「剛剛執行完的行」。遇到 🤔 問題時,先猜綠色箭頭會跳到第幾行,再按下一步對答案。

畫面說明:特別注意兩個時刻。一是第 5 行 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 的標籤會怎麼變。

畫面說明:① 只走了第 3、4 行就離開分級那一段(4 個步驟就有答案)。② 把最寬的條件 score >= 60 放在最前面,95 分在第一道關卡就被攔成 C,後面的 elif 連看都沒看,獎學金也跟著沒了。③ 的三個 if 彼此獨立,每一個都會重新檢查,所以 level 這張標籤被撕下來改貼了兩次(A → B → C),最後留下的是最後一次貼的 C。請在 ③ 裡盯著右邊記憶體:每次改貼,舊的字串方塊都會淡出。

🎮 互動實驗室三:預測輸出卡

每張卡是一小段程式,請先在心裡跑一遍,再選出它會印出什麼。選完會顯示對錯、真正的執行結果和原因。所有輸出都是用 Python 3.13 實際執行得到的。卡片涵蓋比較運算、= 與 ==、大小寫、and/or/not、真假值、縮排、elif 順序與巢狀 if。

已作答 0 / 11答對 0
畫面說明:還沒有作答。建議從第 1 張開始,前 6 張練比較與 and/or/not,後 5 張練真假值、縮排與 elif。

📘 原理補完

1. 六個比較運算子:每一個都是在問是非題

Python 官方文件把 <、>、==、>=、<=、!= 叫做比較運算子,它們的結果一律是布林值 True 或 False。注意兩個字母大寫開頭:寫成 true 會得到 NameError,因為 Python 把它當成一個還沒建立的名字。下表的結果都是實際執行得到的。

運算子讀法例子結果新手容易誤會的地方
==等於嗎?7 == 7.0True兩個等號才是「比較」;一個等號 = 是第 2 課的「貼標籤」
!=不等於嗎?10 != 10False驚嘆號在前、等號在後,中間不能有空白
>、<大於嗎?小於嗎?7 > 3True剛好相等時是 False:5 > 5 不成立
>=、<=大於或等於嗎?20 >= 20True等號一定寫在後面;=> 是語法錯誤
==(字串)一模一樣嗎?"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)。電腦執行的步驟是:

  1. 算出 if 後面的條件。如果是 True,執行它底下的區塊,做完就離開整組判斷,跳到這一組後面第一行沒有縮排的程式。
  2. 如果是 False,跳過那個區塊,去算下一個 elif 的條件,用同樣的方式處理。
  3. 所有條件都是 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
✅ 由嚴到寬(95 分) ❌ 由寬到嚴(95 分) if score >= 90: elif score >= 75: elif score >= 60: else: if score >= 60: elif score >= 75: elif score >= 90: else: A C True True 後面的關卡不會再檢查 ≥ 90 的關卡永遠輪不到 四個分數的結果 四個分數的結果 95→A 80→B 65→C 40→D 95→C 80→C 65→C 40→D
同樣四道關卡,只是排列順序不同。右邊把最寬鬆的「60 分以上」放第一,所有及格的分數都在這裡被攔下來,A 和 B 永遠不會出現。Python 不會報錯,因為語法完全正確,這種「程式能跑、答案卻錯」的情況叫邏輯錯誤,第 10 課會再談。

實驗室二的 ③ 示範了另一種常見寫法:把 elif 全部寫成 if。下表整理幾種寫法的差別,判斷一段程式「會執行幾個區塊」時,先看它們是不是同一組。

寫法會檢查幾個條件最多執行幾個區塊適合的情況
if 單獨一個1 個0 或 1 個「有需要才多做一件事」,例如離島加收運費
if … else1 個剛好 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 鍵時會自動插入空白。真正的規則是:同一個區塊裡每一行的縮排必須一樣多。

「關窗」有縮排 「關窗」沒縮排 if temp > 25: print("開冷氣") print("關窗") print("完成") if temp > 25: print("開冷氣") print("關窗") print("完成") if 的區塊 if 的區塊 temp = 18 時的輸出 temp = 18 時的輸出 完成關窗完成 只差 4 個空白:左邊「關窗」跟著 if 一起被跳過,右邊不管天氣都會關窗
兩段程式的文字幾乎一樣,差別只在第 3 行前面有沒有 4 個空白。黃色底是 if 的區塊,左邊的虛線是縮排的位置。條件 18 > 25 不成立時,整個黃色區塊都會被跳過。

4. and、or、not:把好幾個條件組起來

要同時看兩個條件時,用邏輯運算子(logical operator)。A and B 是「A 而且 B」,兩個都成立才成立;A or B 是「A 或者 B」,有一個成立就成立;not A 把真假翻過來。它們都用英文單字寫,不是符號:寫成 && 或 ||(其他語言的寫法)在 Python 裡是語法錯誤。

A and B:兩個都要成立 A or B:一個成立就夠 B 是 TrueB 是 False B 是 TrueB 是 False A 是 TrueA 是 False A 是 TrueA 是 False True False False False True True True False 只有左上角是 True 只有右下角是 False not:翻面 not True → Falsenot False → True 例:age >= 18 and member 年滿 18 歲「而且」是會員,才可以借
and 和 or 的結果整理成表格。記憶方式:and 像兩道都要刷卡的門,任何一道刷不過就進不去;or 像兩個入口,任何一個開著就進得去。

還有一個實用的細節: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 合併條件,或是先把「不處理」的情況排除掉。

if member: if total >= 1000: print("免運") else: print("會員運費 30") else: print("運費 60") 同一條虛線上的 if、else 是同一組 member 成立? total >= 1000? 運費 60 免運 會員運費 30 TrueFalseTrueFalse 橘色箭頭:member = True、total = 800 的路線(示意)
左邊的程式和右邊的判斷樹是同一件事。外層 if 決定走左邊還是右邊,只有走進左邊的會員,才會碰到內層的 total >= 1000。不是會員的人,內層那一組連看都不會看。

7. 真假值:不是 True/False 的東西也能放進 if

if 後面不一定要寫比較運算。把一個數字、字串或串列直接放進去,Python 會把它當成 True 或 False 來看,這叫做真假值(truthiness,官方文件稱為 truth value testing)。規則可以濃縮成一句:「零」和「空」是假的,其他幾乎都是真的。可以用 bool(值) 直接查。

if x: 的時候,x 會被當成…… False(假值) True(真值) False 0 0.0 None "" [] {} 數字零、空字串、空串列、空字典、 None(「沒有值」) True 1 -1 0.1 " " "0" "False" [0] 不是零的數字、有內容的字串(就算 只是一個空白或 "0")、有東西的串列 "0" 和 [0] 是真的:字串裡有一個字元、串列裡有一格,它們都不是「空」的
左右兩邊都是用 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