4. 深入了解流程控制

除了剛才介紹的 while,這章節還會介紹一些 Python 的陳述式語法。

4.1. if 陳述式

或許最常見的陳述式種類就是 if 了。舉例來說:

>>> x = int(input("Please enter an integer: "))
Please enter an integer: 42
>>> if x < 0:
...     x = 0
...     print('Negative changed to zero')
... elif x == 0:
...     print('Zero')
... elif x == 1:
...     print('Single')
... else:
...     print('More')
...
More

在陳述式中,可以沒有或有許多個 elif 敘述,且 else 敘述並不是必要的。關鍵字 elif 是「else if」的縮寫,用來避免過多的縮排。一個 if ... elif ... elif ... 序列可以用來替代其他程式語言中的 switchcase 陳述式。

如果你要將同一個值與多個常數進行比較,或者檢查特定的型別或屬性,你可能會發現 match 陳述式也很有用。更多的細節,請參閱 match 陳述式

4.2. for 陳述式

在 Python 中的 for 陳述式有點不同於在 C 或 Pascal 中的慣用方式。相較於只能疊代 (iterate) 一個等差數列(如 Pascal),或給予使用者定義疊代步驟與終止條件(如 C),Python 的 for 陳述式疊代任何序列(list 或者字串)的元素,順序與它們出現在序列中的順序相同。例如(無意雙關):

>>> # 測量一些字串:
>>> words = ['cat', 'window', 'defenestrate']
>>> for w in words:
...     print(w, len(w))
...
cat 3
window 6
defenestrate 12

在疊代一個集合的同時修改該集合的內容,很難取得想要的結果。比較直觀的替代方式,是疊代該集合的副本,或建立一個新的集合:

# 建立一個範例集合
users = {'Hans': 'active', 'Éléonore': 'inactive', '景太郎': 'active'}

# 策略:對副本進行疊代
for user, status in users.copy().items():
    if status == 'inactive':
        del users[user]

# 策略:建立一個新集合
active_users = {}
for user, status in users.items():
    if status == 'active':
        active_users[user] = status

4.3. range() 函式

如果你需要疊代一個數列的話,使用內建 range() 函式就很方便。它可以生成一等差數列:

>>> for i in range(5):
...     print(i)
...
0
1
2
3
4

給定的結束值永遠不會出現在生成的序列中;range(10) 生成的 10 個數值,即對應存取一個長度為 10 的序列內每一個項目的索引值。也可以讓 range 從其他數值開始計數,或者給定不同的公差(甚至為負;有時稱之為 step):

>>> list(range(5, 10))
[5, 6, 7, 8, 9]

>>> list(range(0, 10, 3))
[0, 3, 6, 9]

>>> list(range(-10, -100, -30))
[-10, -40, -70]

欲疊代一個序列的索引值,你可以搭配使用 range()len() 如下:

>>> a = ['Mary', 'had', 'a', 'little', 'lamb']
>>> for i in range(len(a)):
...     print(i, a[i])
...
0 Mary
1 had
2 a
3 little
4 lamb

然而,在多數的情況,使用 enumerate() 函式將更為方便,詳見迴圈技巧

如果直接印出一個 range 則會出現奇怪的輸出:

>>> range(10)
range(0, 10)

在很多情況下,由 range() 回傳的物件表現得像是一個 list(串列)一樣,但實際上它並不是。它是一個在疊代時能夠回傳所要求的序列中所有項目的物件,但它不會真正建出這個序列的 list,以節省空間。

我們稱這樣的物件為 iterable(可疊代物件),意即能作為函式及架構中可以一直取得項目直到取盡的對象。我們已經了解 for 陳述式就是如此的架構,另一個使用 iterable 的函式範例是 sum()

>>> sum(range(4))  # 0 + 1 + 2 + 3
6

待會我們可以看到更多回傳 iterable 和使用 iterable 為引數的函式。在資料結構章節中,我們會討論更多關於 list() 的細節。

4.4. breakcontinue 陳述式

break 陳述式,終止包含它的最內部 forwhile 迴圈:

>>> for n in range(2, 10):
...     for x in range(2, n):
...         if n % x == 0:
...             print(f"{n} equals {x} * {n//x}")
...             break
...
4 equals 2 * 2
6 equals 2 * 3
8 equals 2 * 4
9 equals 3 * 3

continue 陳述式讓所屬的迴圈繼續執行下個疊代:

>>> for num in range(2, 10):
...     if num % 2 == 0:
...         print(f"Found an even number {num}")
...         continue
...     print(f"Found an odd number {num}")
...
Found an even number 2
Found an odd number 3
Found an even number 4
Found an odd number 5
Found an even number 6
Found an odd number 7
Found an even number 8
Found an odd number 9

4.5. 迴圈的 else 子句

forwhile 迴圈中,break 陳述句可能與 else 子句配對。如果迴圈完成而沒有執行 breakelse 子句會被執行。

for 迴圈中,else 子句會在迴圈完成最終的疊代後執行。

while 迴圈中,它會在迴圈條件變為 false 後執行。

在任何一種迴圈中,如果迴圈由 break 終止,則不會執行 else 子句。當然其他提早結束迴圈的方式(例如 return 或引發例外)也會跳過 else 子句的執行。

下面的 for 迴圈對此進行了舉例說明,該迴圈用以搜尋質數:

>>> for n in range(2, 10):
...     for x in range(2, n):
...         if n % x == 0:
...             print(n, 'equals', x, '*', n//x)
...             break
...     else:
...         # 迴圈結束但沒有找到因數
...         print(n, 'is a prime number')
...
2 is a prime number
3 is a prime number
4 equals 2 * 2
5 is a prime number
6 equals 2 * 3
7 is a prime number
8 equals 2 * 4
9 equals 3 * 3

(沒錯,這是正確的程式碼。請看仔細:else 子句屬於 for 迴圈,並非 if 陳述式。)

理解 else 子句的一個方法是將它想像成與迴圈內的 if 配對。當迴圈執行時,它會運行如 if/if/if/else 的序列。if 在迴圈內,會遇到多次。如果條件曾經為真,break 就會發生。如果條件從未為真,迴圈外的 else 子句就會執行。

else 子句用於迴圈時,相較於搭配 if 陳述式使用,它的行為與 try 陳述式中的 else 子句更為相似:try 陳述式的 else 子句在沒有發生例外 (exception) 時執行,而迴圈的 else 子句在沒有任何 break 發生時執行。更多有關 try 陳述式和例外的介紹,見