
前言「寫條件分支」看起來是最沒技術(shù)含量的活但代碼質(zhì)量的分水嶺恰恰在這里。同一個業(yè)務(wù)規(guī)則有人寫出五層嵌套的if有人用三行if/elif加一個字典就清爽收尾。區(qū)別不在于聰明而在于是否知道 Python 提供了哪些分支寫法、各自適合什么場景。初學(xué)者最常見的三個誤解是以為and/or返回的是True/False其實返回的是參與運算的操作數(shù)本身以為match就是 C 的switch其實沒有貫穿、還能匹配結(jié)構(gòu)以及以為x or default是安全的默認(rèn)值寫法當(dāng)x是0或空字符串時會被錯誤替換。本文按「從簡單到復(fù)雜」的順序講六種條件分支寫法并給出各自該在什么時候用。文中的match語句需要 Python 3.10 及以上其余寫法在 Python 3.8 及以上都能跑。一、基礎(chǔ)if / elif / else最直接的寫法。要點是命中即停所以判斷順序就是優(yōu)先級順序。# 適用于 Python 3 全版本def http_status_text(code):if code 200:return OKelif code 301:return Moved Permanentlyelif code 404:return Not Foundelif 500 code 600:return Server Errorelse:return Unknown這里用到了鏈?zhǔn)奖容^500 code 600它等價于500 code and code 600但更短也更不易寫錯。二、條件表達(dá)式三元運算符當(dāng)分支只是為了「二選一取值」時用條件表達(dá)式比寫四行if/else更清晰# 適用于 Python 3 全版本age 17label 成年 if age 18 else 未成年print(label) # 未成年形式是「真值 if 條件 else 假值」順序和 C 的條件 ? 真值 : 假值相反這是高頻寫錯點。它適合短表達(dá)式嵌套多層會立刻變得難讀# 適用于 Python 3 全版本# 不推薦嵌套三元可讀性差grade A if s 90 else (B if s 80 else C)多層判斷還是回到if/elif/else。三、布爾運算的短路與返回值and和or有短路特性而且返回的是操作數(shù)本身a or ba為真就返回a否則返回ba and ba為假就返回a否則返回b# 適用于 Python 3 全版本print(0 or 默認(rèn)) # 默認(rèn)print(x or 默認(rèn)) # xprint(0 and 1 / 0) # 0右側(cè)未求值不會除零print(abc and 42) # 42利用返回值可以寫出簡短的默認(rèn)值邏輯但要注意假值判斷的陷阱or會把0、、[]也當(dāng)成「沒有值」。如果一個合法值就是0必須改用顯式的is None判斷# 適用于 Python 3 全版本def with_default(value, default100):# 錯誤示范value 為 0 時也會被替換# return value or default# 正確只在確實是 None 時才用默認(rèn)值return default if value is None else valueprint(with_default(0)) # 0print(with_default(None)) # 100四、衛(wèi)語句提前返回消除嵌套深層嵌套的根因通常是「所有失敗情況都往里縮進(jìn)」。反過來寫不合格的輸入提前 return把主邏輯留在最外層。# 適用于 Python 3 全版本# 不推薦嵌套三層def price_bad(user, item):if user is not None:if user.is_active:if item.stock 0:return item.priceelse:return -1else:return -2else:return -3# 推薦衛(wèi)語句提前返回def price_good(user, item):if user is None:return -3if not user.is_active:return -2if item.stock 0:return -1return item.price五、字典分派把「值相等」的分支換成查表當(dāng)分支條件是「某個值和一批常量逐個比較」時用字典把「動作」或「結(jié)果」映射起來比一長串elif更好維護(hù)。# 適用于 Python 3 全版本def apply_op(op, a, b):operations {: lambda x, y: x y,-: lambda x, y: x - y,*: lambda x, y: x * y,}func operations.get(op)if func is None:raise ValueError(f不支持的運算符{op})return func(a, b)print(apply_op(*, 6, 7)) # 42注意用.get()而不是operations[op]直接下標(biāo)在沒有該鍵時會拋KeyError除非這正是你想要的錯誤。字典分派的前提是分派鍵是可哈希且可比較的常量不適合范圍判斷。六、match 語句結(jié)構(gòu)化模式匹配Python 3.10從 Python 3.10 起語言引入了match/casePEP 634。它不是switch的翻版沒有貫穿fall-through命中一個case就結(jié)束能匹配結(jié)構(gòu)序列、映射、類實例不只是值_是通配符相當(dāng)于默認(rèn)分支支持守衛(wèi)子句case ... if 條件:# 適用于 Python 3.10def describe(command):match command.split():case [go, direction]:return f向 {direction} 前進(jìn)case [look]:return 你環(huán)顧四周case [take, item] if len(item) 3:return f拿起一個較長的物品{item}case [take, item]:return f拿起 {item}case _:return 無法識別的指令print(describe(go north)) # 向 north 前進(jìn)print(describe(take)) # 無法識別的指令需要在 Python 3.10 以下運行時同一段邏輯只能用if/elif判斷l(xiāng)en(parts)和各位置的值來手寫。實戰(zhàn)把六種寫法用在一個「評分系統(tǒng)」里# 適用于 Python 3.10from dataclasses import dataclassdataclassclass Submission:author: strscore: int | None None # 3.10 的聯(lián)合類型寫法def grade_of(score):if score is None:return 未評分if score 90:return Aif score 80:return Bif score 60:return Creturn Ddef label(submission):if submission.score is None:return f{submission.author}待評分status 優(yōu)秀 if submission.score 90 else 已評分return f{submission.author}{grade_of(submission.score)}{status}def main():subs [Submission(Ada, 95),Submission(Bob, 72),Submission(Cy, None),]for s in subs:print(label(s))if __name__ __main__:main()輸出依次是AdaA優(yōu)秀、BobC已評分、Cy待評分。注意int | None這種聯(lián)合類型寫法需要 Python 3.10 及以上在 3.9 里要寫typing.Optional[int]3.8 同理。常見坑點與混淆?if code 200:?if code 200:Python 3 里用在條件位置是語法錯誤以為and/or返回布爾值? 用x and y的結(jié)果去做is True判斷 ? 它們返回操作數(shù)本身a and b得到b不是True用or提供默認(rèn)值把合法假值也替換掉?return value or default當(dāng)value是0//[]時誤用默認(rèn)值 ?return default if value is None else value把match當(dāng)switch用? 期待case會像 C 那樣向下貫穿或以為它只比常量 ?match無貫穿、能做結(jié)構(gòu)匹配、_是通配符需要 Python 3.10字典分派時直接用下標(biāo)導(dǎo)致KeyError?operations[op](a, b)? 先func operations.get(op)判空后再調(diào)用或明確用 try/except 捕獲深層嵌套而不使用衛(wèi)語句? 把失敗分支一層層縮進(jìn)到看不見主邏輯 ? 不合格輸入提前return主邏輯留在最外層把范圍判斷寫進(jìn)字典分派? 試圖用{range(0, 60): D, range(60, 80): C}直接查表 ?range對象相等比較不是區(qū)間匹配范圍判斷仍用if/elif或?qū)iT的區(qū)間表條件表達(dá)式順序?qū)懛?label 未成年 if age 18 else 成年? 真值在前l(fā)abel 成年 if age 18 else 未成年總結(jié)寫法適用場景注意事項if/elif/else通用分支、范圍判斷命中即停順序即優(yōu)先級條件表達(dá)式二選一取值真值在前勿嵌套多層and/or短路簡單默認(rèn)值、守衛(wèi)返回操作數(shù)假值會誤觸發(fā)衛(wèi)語句消除深層嵌套失敗提前返回字典分派常量到結(jié)果的映射用.get()防KeyErrormatch結(jié)構(gòu)匹配3.10無貫穿_為通配符判斷該用哪種寫法只需要問自己兩個問題分支條件是「值相等」還是「范圍/結(jié)構(gòu)」值是常量就考慮字典分派是結(jié)構(gòu)就用match是范圍就用if/elif。至于嵌套過深先想到衛(wèi)語句而不是繼續(xù)往里縮進(jìn)。