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 ... 序列可以用來替代其他程式語言中的 switch 或 case 陳述式。
如果你要將同一個值與多個常數進行比較,或者檢查特定的型別或屬性,你可能會發現 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. break 和 continue 陳述式¶
break 陳述式,終止包含它的最內部 for 或 while 迴圈:
>>> 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 子句¶
在 for 或 while 迴圈中,break 陳述句可能與 else 子句配對。如果迴圈完成而沒有執行 break,else 子句會被執行。
在 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 陳述式和例外的介紹,見