while 迴圈、for 迭代
April 14, 2022Python提供 while
迴圈,可根據指定條件式來判斷是否執行迴圈本體。
while 迴圈
先來看個很無聊的遊戲,看誰可以最久不碰到 5 這個數字:
from random import randint
while (number := randint(0, 9)) != 5:
print(number)
print('我碰到 5 了....Orz')
random
模組的 randint
會隨機產生 0 到 9 的整數,這邊運用了 Python 3.8 新增的海象運算子指定給 number
,加上括號表示指定運算式會先運算,之後再判斷 number
是否為 5,若判斷為 True
就執行迴圈。一個參考的執行結果如下:
1
1
9
8
7
我碰到 5 了....Orz
運用迴圈的場合,可能是海象運算子的使用,就這個例子來說,若不使用海象運算子,要有相同執行結果,可以如下撰寫:
from random import randint
number = 0
while number != 5:
number = randint(0, 9)
if number == 5:
break
print(number)
print('我碰到5了....Orz')
在迴圈中若執行 break
,就會中斷 while
迴圈,因此在 number
為 5 的情況下,print(number)
不會被執行;相對之下,使用海象運算子的版本比較簡潔。
在 while
迴圈中,還可以使用 continue
,遇到 continue
的話,該次不執行後續的程式碼,直接進行下次迴圈。
for in 迭代
如果想循序迭代某個序列,例如字串、list
、tuple
,可以使用 for in
陳述句。例如,迭代使用者提供的命令列引數,轉為大寫後輸出。
import sys
for arg in sys.argv:
print(arg.upper())
要被迭代的序列,是放在 in
之後,對於字串、list
、tuple
等具索引特性的序列,for in
會依索引順序逐一取出元素,並指定給 in
之前的變數。一個執行結果如下:
>python uppers.py justin monica irene
UPPERS.PY
JUSTIN
MONICA
IRENE
如果在迭代的同時,需要同時提供索引資訊,那麼有幾個方式,例如使用 range
產生一個指定的數字範圍,使用 for in
進行迭代,再利用迭代出來的數字作為索引。例如:
>>> for i in range(len(name := 'Justin')):
... print(i, name[i])
...
0 J
1 u
2 s
3 t
4 i
5 n
>>>
range
的形式是 range(start, stop[, step])
,start
省略時,預設是 0,step
是步進值,省略時預設是 1,因此上例中, range(len(name := 'Justin'))
的結果,會產生 0 到 5 的數字。
你也可以使用 zip
,將兩個序列的各元素,像拉鏈般一對一配對(這就是為什麼它叫zip的原因,實際上 zip
可以接受多個序列),產生一個新的 list
,當中每個元素都是個 tuple
,包括了配對後的元素。
>>> list(zip([1, 2, 3], ['one', 'two', 'three']))
[(1, 'one'), (2, 'two'), (3, 'three')]
>>>
zip
會傳回一個 zip
物件,這個物件實際上還不包括真正配對後的元素,也就是具有惰性求值的特性(range
產生的 range
物件也是)。zip
物件可以使用 for in
迭代,因此若迭代時需要索引資訊,可以如下:
name = 'Justin'
for i, c in zip(range(len(name)), name):
print(i, c)
在這邊還使用了 tuple
拆解的特性,將每一對 tuple
的元素,拆解指定給 i
與 c
變數。
實際上,若真的要迭代時具有索引資訊,建議使用 enumerate
而不是 range
,enumerate
會傳回 enumerate
物件,一樣具有惰性求值特性,且可使用 for in
迭代,enumerate
可取得 tuple
元素,例如:
>>> list(enumerate('Justin'))
[(0, 'J'), (1, 'u'), (2, 's'), (3, 't'), (4, 'i'), (5, 'n')]
>>>
因此,迭代時具有索引資訊,也可以使用以下方式:
for i, c in enumerate('Justin'):
print(i, c)
預設的情況下,enumerate
會從 0 開始計數,如果想從其他數字開始,可以在 enumerate
的第二個引數指定。例如從 1 開始:
for i, c in enumerate('Justin', 1):
print(i, c)
之後會看到,只要是實作了 __iter__
方法的物件,都可以透過 __iter__
方法傳回一個迭代器(Iterator),這個迭代器可以使用 for in
迭代,像是之前的 range
、zip
、enumerate
物件就是如此。
set
也實作了 __iter__
方法,因此可以進行迭代,不過因為 set
是無序的,只能迭代出元素,但不一定是你想要的順序。
至於想要迭代 dict
鍵值的話,可以使用它的 keys
、values
或 items
方法,它們各會傳回 dict_keys
、dict_values
、dict_items
物件,都實作了 __iter__
方法,因此也可以使用 for in
迭代。舉例來說,來同時迭代 dict
的鍵值:
>>> passwds = {'Justin' : 123456, 'Monica' : 54321}
>>> for name, passwd in passwds.items():
... print(name, passwd)
...
Justin 123456
Monica 54321
>>>
因為 dict_items
的元素是 tuple
,各包括了一對鍵、值,同樣地,這邊使用了 tuple
拆解的特性,將 tuple
的鍵、值拆解給 name
與 passwd
變數。如果直接針對 dict
進行 for in
迭代,預設會進行鍵的迭代。
for
也可以使用 break
來中斷迭代,在 for in
迭代遇到 continue
的話,該次不執行後續的程式碼,直接進行下次迭代。
以下是利用 continue
的特性,實作出一個只顯示小寫字母的程式:
for letter in input('輸入一個字串:'):
if letter.isupper():
continue
print(letter, end='')
這個範例在遇到大寫字母時,就會執行 continue
,因此該次不會執行 print
。一個執行範例如下:
輸入一個字串:This is a Question!
his is a uestion!
while
、for in
都有可與 else
配對的形式,然而不建議使用,因為寫出來的程式碼可讀性不佳,這邊就不討論了。