2.11. 條件判斷

條件判斷(conditional)只有在某個測試評估為真時才會執行一段程式碼區塊。其關鍵字是 if,後面可選擇性地接一個或多個 elif(即「else if」)分支,以及最後的 else

n = 42

if n > 0:
    print("positive")
elif n < 0:
    print("negative")
else:
    print("zero")

每個分支的主體就是縮排在它底下的所有內容(依慣例為四個空格)。Python 會依序走訪各分支,執行第一個測試為真的分支,並略過其餘的。else 區塊只有在前面每個測試都為假時才會執行;它永遠是可選的。

一張展示 if/elif/else 的流程圖:兩個菱形決策 測試各自帶有其主體,當兩個測試都為假時 落入最後的 else 主體。

永遠只會有一個分支執行。測試會由上而下評估,直到其中一個成功為止;其餘的會被略過。

2.11.1. 真假值

if 中的測試不一定要回傳 TrueFalse ——任何值都會被視為為真(truthy)或為假(falsy)。為假的值有:

  • FalseNone

  • 數字零:00.0

  • 空的序列與容器:""[](){}b""

其他一切皆為真。這讓你能寫出精簡的測試:

if name:                     # false on empty string
    print("hello", name)

if items:                    # false on empty list, dict, etc.
    process(items)

請注意,真假值會改變語意。if value:if value is not None: 並不相同——前者在 value0"" 時也會為假。當你真正的意思是「這是否恰好為 None」時,請明確使用 is Noneis not None

2.11.2. 三元運算式

條件判斷可以出現在運算式之中:

label = "even" if n % 2 == 0 else "odd"

讀作「如果 n % 2 == 0label"even"否則"odd"。」這對單行寫法很方便;但只要超過一行的內容,完整的 if 陳述句會更容易閱讀。

2.11.3. 巢狀與提早回傳

條件判斷可以任意巢狀得很深,但每多一層縮排都會讓函式更難閱讀。下面的範例在進行真正的工作前檢查了四個條件,使得有用的那一行被埋在四層縮排之內:

def process(item):
    if item is not None:
        if item.is_valid():
            if item.size() > 0:
                if item.owner == "me":
                    return do_the_work(item)
    return None

有兩種模式能讓這類程式碼變得扁平。

2.11.3.1. 用提早回傳作為守衛

先處理每個「直接退出」的情況,各自搭配自己的 return,這樣主要邏輯就能留在外層縮排。每個守衛都讀作「這不是我們要處理的情況;離開」:

def process(item):
    if item is None:
        return None
    if not item.is_valid():
        return None
    if item.size() == 0:
        return None
    if item.owner != "me":
        return None
    return do_the_work(item)

「主路徑」現在是函式底部的一行,而不是被埋在四層之內。這種風格有時稱為守衛子句(guard clause)模式。

2.11.3.2. 用 and / or 組合測試

當數個條件都必須成立才進入同一個分支時,請用 and 來組合它們,而不要使用巢狀。各自獨立就能觸發該分支的條件則用 or 組合:

# all must hold -- use `and`
if user.is_admin() and user.has_permission("write") and not locked:
    save()

# any one of them is enough -- use `or`
if c == " " or c == "\t" or c == "\n":
    whitespace_count += 1

兩種形式都會短路求值,因此右側成本較高的檢查只有在左側成本較低的檢查尚未確定結果時才會執行。