更新日期: 2022 年 12 月 20 日
這篇文章要來談談迴圈(loop),Python for loop 是不少初學程式語言的人,遇到就覺得腦袋當機,無法理解與突破的「迴圈」,繞來繞去就是看不懂。
覺得挫折的你可能會覺得,為什麼程式語言要弄得那麼複雜,學了一堆資料型態,還要學迴圈?創造程式語言的人根本是找你麻煩。
如果你有這個想法,可能誤會大了。
假如「迴圈」有感情,此刻它的內心大概滿滿的委屈,好心幫忙,卻被潑了一桶髒水,好心沒好報。
迴圈到底會幫什麼忙呢?
Python for
迴圈,可以幫你執行類似的重複事情。
重複的事?聽起來無關緊要,來看看一個小例子,讓你對於重複的事稍微有個概念。
這個例子是這樣的,用Python印出1到10。
很簡單,你只要會使用print()
函式,打出第一個print()
,再複製9個貼上,並依序填入1到10的數字,輕鬆搞定。
>>> print(1)
>>> print(2)
>>> print(3)
>>> print(4)
>>> print(5)
>>> print(6)
>>> print(7)
>>> print(8)
>>> print(9)
>>> print(10)
執行程式後得到的結果:
1
2
3
4
5
6
7
8
9
10
一行一行的打出print()
,並在括號內填入你要輸出的數值,是個方法沒錯。
但如果情境變了,要你打出1到1000之間的每個整數,或是1到1億之間的每一個整數,一個都不能漏。
光是要你按鍵盤1億次,你的手指頭可能都負荷不了,還會在複製貼上的過程中,懷疑人生。
這個時候,迴圈可以來拯救你,用兩三行程式碼,取代你單獨使用print()
時所需要的10行、1000行或是1億行的程式碼。
Python世界的迴圈工具箱中,有兩種工具供你使用,分別是for
陳述句及while
陳述句,都可以幫你執行重複的事情。
我們就直接來看for
陳述句及while
陳述句,如何幫你執行重複的工作,讓你暫時不再懷疑人生(暫時?沒錯,人生長長數十載,你總是會一直碰到關卡,不斷地前進,在懷疑與解惑中揮灑歲月)。
首先我們來看一下,for
陳述句如何幫助你:
>>>for i in range(1,11):
>>> print(i)
while
陳述句如何幫你:
>>>count = 1
>>>while count <11:
>>> print(count)
>>> count += 1
這兩個作法,都會跟你使用10個 print()
函式,來印出1到10之間的每一個整數的結果一樣。
瞭解迴圈可以幫你省不少力氣後,你是不是有興趣學習迴圈如何操作了。
本文將先介紹迴圈的基礎觀念,接著介紹 Python中的for
迴圈,while
將另文介紹。記得喔,for
與while
都要小寫!
如果你想直接閱讀for
陳述句操作,利用快速閱讀,就可以跳到你有需要的段落。
迴圈是什麼?
迴圈(loop)是編寫程式時很重要的概念,讓電腦自動完成重複工作的常見方式。
但不得不說,迴圈也是初學程式語言時,不易跨過的門檻,無法理解程式碼是怎麼運作的,看到 for 就覺得頭大。
不灰心,讓我們從一般英文的用法來了解 for 陳述句吧,先喚起你腦中對 for 這個英文單字意思的記憶。
英語世界的 for 代表的意思
The gift is for you.
這禮物是給你的。
這裡的 for 是給的意思,是剛學英文時最常接觸到的意思。
不過,其實for可以代表很多意思,當介係詞時,除了代表「給」的意思,也可以代表「關於」的意思。
You are qualified for that job.
關於那個工作,你是符合資格的。
而「關於」的意思,與for
陳述句的概念較為相近,你在 Python 中看到for
可以先把它理解為「關於」。
Python世界中的 for 迴圈
以前面所舉印出1到10的例子來說。
>>>for i in range(1,11):
>>> print(i)
這兩行程式碼你可以這樣理解:
關於1到11的範圍內的每一個項目,依序將每個項目指派給變數i,並將每一個i印出。
如果你仍然看不懂程式碼,你可以連結到Python tutor 程式碼視覺化網站,看一下程式碼是怎麼運作的,幫助你理解。
進到網站點選next就可以控制程式運作的每一個步驟,關於Python Tutor的使用教學,可以閱讀《看不懂程式碼?免費「程式語言家教」在這裡!》這篇文章,Python Tutor 就是你的免費程式語言家教。
接下來,我們一個一個來看這兩行程式碼中所有元素的意思。
>>>for i in range(1,11):
>>> print(i)
for
首先for
就是代表for
陳述句,這個陳述的尾端一定要加上冒號:
,Python才會知道有重複性質的工作要做囉,而且要做的事情必須縮排呈現,才是for
的工作範圍。
for
陳述句的功能是要幫你執行性質類似的重複工作,印出1到10的每個整數,就是從1開始,後一個數字是前一個數字加1(就是間隔1),這樣的重複性工作。
變數 i
i是一個任意的變數,它代表你在跑迴圈時所抓到的資訊,即迭代獲取的資料。
你可以將這個任意的變數,想像為你現在臨時要找人手幫你搬大型數字道具,1到10依序搬到大螢幕前,你可以隨便抓一個叫小明的傢伙,幫你一個一個搬過去,這時小明就是代表那個任意的變數。在本文的例子中,我們找了個叫做 i 的傢伙來做這件事。
而在1到10的數字道具間,依序的搬動就是「迭代」,一個一個的歷覽。
in 成員運算子
in
是程式語言中常見的運算子,基本上使用 for 陳述句都會與in
運算子搭配。
in
算子的工作是幫助for
陳述句界定迭代的範圍,in
就代表在什麼範圍內的意思。
range(1, 11)
range()
是for
陳述句常用的迭代範圍。
in
代表在什麼範圍內,而迭代的範圍就接在in
的後方,這裡的例子以range(1, 11)
表示。
如果你無法理解迭代的範圍是什麼,就想成是迴圈跑的範圍,「迭代」這個詞對初學者而言,不是很好懂,不過你慢慢熟悉程式語言後就會瞭解。
range 這個單字,單看他的英文意思就是範圍的意思。例子中的range(1, 11)
代表 1到10,11是代表到此為止的終點,不准越過,也不准涵蓋進去。
for 陳述句冒號後,有縮排的程式碼們
print(i)
在for陳述句的下一行,以縮排呈現,代表for陳述句的工作範疇。
print()
這個函式,是初學python的人都有碰過的(沒碰過?沒關係你現在碰上了),print()
就是把括號內的值印出來,執行程式時會顯示在執行結果的螢幕上。
目前說明的例子,就是印出 i,i在1到10的範圍中迭代。
for 陳述句(或稱 for 迴圈)的重點
- 打出
for
- 設置一個臨時變數
- 成員運算子
in
- 可迭代的物件,例如
raneg()
或串列 - 冒號
:
- 冒號後的程式碼縮排
下方簡單的兩行程式碼,便具有這6個要點:
>>>for i in range(1,11):
>>> print(i)
到這裡,你已經擁有for
迴圈的基本知識了,知道for
陳述句的功能就是產生for
迴圈,讓Python按照你的要求,遊歷遍你所指定的範圍。(有點像觀光客,每個景點都要踩到,拍網美照、吃美食)
還是不清楚的話,下方的教學影片可以幫助你理解。
如果要更巧妙的運用for
迴圈,需要增加的知識是:
- 知道可迭代的資料有哪些,就是在
in
後面呈現的資料範圍。 - 迭代後要執行的程式內容,跑迴圈時你希望Python幫你做的事。
- 控制迴圈的工具:
break
與continue
陳述句。
那些東西是常見的Python for 迴圈遊歷範圍呢?
什麼東西可以拿來迭代(iterate)呢?
在前面例子中,range(1, 11)
便是可以被迭代的東西。這些可迭代的東西,稱為「可迭代物」(iterable)。
滿多東西都可以作為「可迭代物」(iterable),以函式(Function)來說的話,常見的包括 range()
、enumerate()
、zip()
、reversed()
、sorted()
;以資料型態來說的話,包括字串(string)、串列(list)、元組(tuple)、字典(dictionary)。
這個段落,將為你說明for
陳述句如何與這些「可迭代物」(iterable)一同運作。
range()函式
for
迴圈中,最為常見的「可迭代物」當屬 range()
函式莫屬。
想要了解range()
函式與for
迴圈如何搭配,稍微瞭解range()
怎麼操作,你會更得心應手。
range()函式基本瞭解
range()
函式可以輸入三個參數,彼此以逗號隔開,參數只能是數值或是代表index
的物件:
range(起始值, 終點值, 間隔值)
起始值與間隔值都可以省略,省略時,起始值預設為0,間隔值預設為1。
終點值不可省略,終點值是不可納入的數值,只會取到其前一位。
例如 range(5)
就是省略起始值與間隔值,代表0到4的值,5是終點值不會納入。
由於range()
函式返回的值是一個可迭代的值,所以下方的例子將之轉換為串列;當你執行print(range(5))
,返回的值是range(0, 5)
。
>>>the_range = (list(range(5)))
>>>print(the_range)
[0, 1, 2, 3, 4]
range()
函式可以指定一個區間特定間隔的數值。間隔值除了顯示間隔外,也可以利用負號來表示反序。
下方的例子,起始值為2,終點值是20,間隔值是2。
>>>the_range = (list(range(2, 20, 2)))
>>>print(the_range)
[2, 4, 6, 8, 10, 12, 14, 16, 18]
如果你想試看看間隔值是負的-
,請留意你的終點值在推移的順序上,必須在起始值之後。
舉一個反序的例子,將間隔值設為-2,因為推移的方向變成以-2的間距推進,原來的終點值20不會在2之後,所以我們將終點值改為-20來示範。
>>>the_range = (list(range(2, -20, -2)))
>>>print(the_range)
[2, 0, -2, -4, -6, -8, -10, -12, -14, -16, -18]
range()與for迴圈的合作
就像最開頭的例子,運用range()
來設定迴圈的遊歷範圍,既然知道range()
有三個參數,下方例子就以填入三個參數來示範。
>>>for i in range(5,100,50):
>>> print(i)
5
55
除了簡單的填入數值,你還可以使用len()
函式,讓 Python 幫你填入數值,這樣的方法有個好處,就是當變數更動時,會自動更新。
>>>word = 'hello'
>>>for i in range(len(word)):
>>> print(word[i])
h
e
l
l
o
比如說,word 變數代表的字串是'hello'
,你可能會用for i in range(5)
,而當變數 word
被改變為'hello word!'
時,你必須手動調整range(5)
為range(11)
,使用range(len(word))
則可以自動更新字串字元數。
len()
函式說明:利用 len() 函式取得字串長度
wrod[i]
說明:取得字串內的部分字元
對range()
還是不清楚?讓 Mosh 簡單的講解給你聽。
enumerate()函式
在英文裡頭,enumerate 的意思是列舉、枚舉,把東西一個一個的列出來。
enumerate()
函式可以列舉出東西所在的位置,以及那個位置上的東西是什麼,換句話說,enumerate()
返回索引及該索引對應的項目。
enumerate()
函式有兩個參數:
enumerate(可迭代物, 索引起始值)
索引起始值可以省略,省略時一律將起始值視為0。
如果你單獨使用enumerate()
函式,Python 只會回傳一個 enumerate的值給你,跟前面提到的range()
相似,單獨使用range()
返回的只有 range 這個資料型態。
>>>word = 'hello'
>>>print(enumerate(word))
>>>print(range(len(word)))
<enumerate object at 0x000002B7222DCB00>
range(0, 5)
enumerate()
函式大多應用於 for 迴圈,當你只有使用一個變數時,返回的值是tuple。
下方的例子,只有使用i這個變數,返回的值是含有索引與對應值的 tuple。
>>>word = 'hello'
>>>for i in enumerate(word):
>>> print(i)
執行程式後我們得到下方的結果,索引值0的位置,對應h;1的位置對應e,以此類推:
(0, 'h')
(1, 'e')
(2, 'l')
(3, 'l')
(4, 'o')
如果你設置了兩個變數,就可以將索引及該索引對應的項目分開取出,取出來的東西,索引是整數,對應的項目則看該項目是什麼資料型態,就是什麼東西。
下方的例子使用了兩個變數index及value,我們只將索引值印出,並用type()
函式顯示資料型態。
>>>word = 'hello'
>>>for index, value in enumerate(word):
>>> print(index, type(index))
得到的結果,每個索引的資料型態都是整數:
0 <class 'int'>
1 <class 'int'>
2 <class 'int'>
3 <class 'int'>
4 <class 'int'>
只取出索引所對應的值,並用type()
函式顯示資料型態。
>>>word = 'hello'
>>>for index, value in enumerate(word):
>>> print(value, type(value))
執行程式結果如下,返回的資料型態是字串:
你可以試著用串列、字典等其他資料型態試試,資料型態會隨著改變。
h <class 'str'>
e <class 'str'>
l <class 'str'>
l <class 'str'>
o <class 'str'>
本文在介紹enumerate()
函式的開頭時,有提到「起始值」這個參數,所以你可以指定索引的起始數值。
下方的例子,就要求起始的數值為5,不是預設的0。
>>>word = 'hello'
>>>for index, value in enumerate(word,5):
>>> print(index, value)
執行程式後,你會看到,對應h的索引變成5了。
5 h
6 e
7 l
8 l
9 o
一個無關enumerate()
函式的小說明,如果你不想使用enumerate()
函式,希望單獨使用for
迴圈得到索引與應對的值,也是可以辦得到的,只是步驟會稍微多一些。
>>>#不使用 enumerate()
>>>i = 0
>>>word = 'hello'
>>>for value in word:
>>> print(i, word[i])
>>> i += 1
>>>
>>>#使用enumerate()
>>>word = 'hello'
>>>for i in enumerate(word):
>>> print(i)
得到的結果如下
0 h
1 e
2 l
3 l
4 o
(0, 'h')
(1, 'e')
(2, 'l')
(3, 'l')
(4, 'o')
看完 for
迴圈常見的函式搭配後,接著,我們來看看它與字串、串列、字典的搭配。
迭代字串(string)
讓 Python 遊歷字串內的每一個字元,下方的例子使用的字串是'Wow!'
。
>>> for i in 'Wow!':
>>> print(i)
W
o
w
!
你也可以把字串指派個一個變數。我們把'Wow! Yik!'
這個字串指派給word
這個變數,在程式碼裡談到word
這個變數,就代表'Wow! Yik!'
字串。
>>> word = 'Wow! Yik!'
>>> for i in word:
>>> print(i)
W
o
w
!
Y
i
k
!
執行程式碼後,每個字元都會印出來,空白字元也會印出來,你看是不是空了一行,那一行是空白字元。
迭代串列(list)
串列的迭代與字串滿像的,字串是迭代字元,串列是迭代項目。
下方的例子中,我們建立一個名為 breakfast 的串列,串列中有早餐會吃的食物名稱。
接著使用for
迴圈,讓 Python 把串列的每個項目依序指派給變數food,並將之印出。
>>>breakfast = ['egg', 'milk', 'apple', 'sandwich']
>>>for food in breakfast:
>>> print(food)
執行程式後得到的結果:
egg
milk
apple
sandwich
你可對串列內的項目再進行操作,讓for
迴圈產出的成果更豐富。
下方的例子就使用slice
,限定串列只包含位置 0 與 1 的 eggs、milk,並用字串方法 upper()
將食物全部大寫,也用了轉義字元\n
增加一行空白行。
>>>breakfast = ['eggs', 'milk', 'apples', 'sandwiches']
>>>for food in breakfast[0:2]:
>>> print('I love ' + food.upper() + ' so much.')
>>> print('I want to have ' + food.upper() + ' as my breakfast!\n')
程式執行的結果:
I love EGGS so much.
I want to have EGGS as my breakfast!
I love MILK so much.
I want to have MILK as my breakfast!
關於串列、字串的操作,如果你有不懂的地方,可以閱讀《Python字串(string)基礎與20種常見操作》、《Python串列(list) 基礎與23個常用操作》這2篇文章。
迭代字典(dictionary)
以字典作為迭代的範圍,只會回傳鍵( key )。如果想迭代值,需要配合字典的方法(Method)使用。
迭代字典的鍵(key)
與字串、串列迭代一樣,把字典放在要迭代的位置上就可以了。
我們為點心製作了一個價格字典,馬芬(Muffin)39元,司康(Scone)25元,餅乾(Biscuit)20元。
將這個字典以for
迴圈進行迭代,變數名稱設為key(隨你想設什麼變數都可,有利於程式碼閱讀為佳)。
>>>desserts = {'Muffin':39, 'Scone':25, 'Biscuit':20}
>>>for key in desserts:
>>> print(key)
執行程式碼,的確都只有印出鍵(key)。
Muffin
Scone
Biscuit
迭代字典的值(value)
如果要迭代的是字典的值,需要使用字典的values()
方法,是 values 有個 s 喔,別打錯了。
同樣以點心的價格字典為例。
>>>desserts = {'Muffin':39, 'Scone':25, 'Biscuit':20}
>>>for value in desserts.values():
>>> print(value)
執行程式碼的結果:
39
25
20
迭代字典的鍵(key)及值(value)
如果要迭代的是字典的鍵與值,就是迭代字典的全部東西,需要使用字典的items()
方法,同樣要留意是 items 有個 s 喔,小心別打錯了。然後還有一點,返回的資料型態是 tuple。
>>>desserts = {'Muffin':39, 'Scone':25, 'Biscuit':20}
>>>for item in desserts.items():
>>> print(item)
執行程式碼的結果:
('Muffin', 39)
('Scone', 25)
('Biscuit', 20)
你還可以將items()
回傳的 tuple 值個別指派給不同變數,稍微改寫一下程式碼,將點心的名稱指派給dessert變數,價格指派給price變數:
>>>desserts = {'Muffin':39, 'Scone':25, 'Biscuit':20}
>>>for dessert, price in desserts.items():
>>> print( dessert + ' $ '+ str(price) )
呈現的結果就可以像下方這樣,比較好閱讀:
Muffin $ 39
Scone $ 25
Biscuit $ 20
具體來說,for迴圈可以做什麼?
前面的例子中,我們都只是使用print()
讓迴圈印出迭代的內容,簡單說明 for 迴圈的常見「可迭代物」。
至於要迴圈實際上幫你做什麼事,取決於你需執行的工作。
也許你需要在使用一個 for 迴圈,形成巢狀迴圈。
例如你對九九乘法表 7 與 8 部分總是記不起來,用 for 巢狀迴圈來列出每一個乘積。
>>>for i in range(7, 9):
>>> for j in range(1, 10):
>>> result = i * j
>>> print(str(i) + ' * ' + str(j) + ' = ' + str(result))
執行程式的結果:
7 * 1 = 7
7 * 2 = 14
7 * 3 = 21
7 * 4 = 28
7 * 5 = 35
7 * 6 = 42
7 * 7 = 49
7 * 8 = 56
7 * 9 = 63
8 * 1 = 8
8 * 2 = 16
8 * 3 = 24
8 * 4 = 32
8 * 5 = 40
8 * 6 = 48
8 * 7 = 56
8 * 8 = 64
8 * 9 = 72
也有可能,你會需要在for
迴圈執行if
陳述句,例如猜數字遊戲的小程式,便會在for
迴圈中加入if
陳述句。
只要是類似的重複事情for
迴圈都可以做,要它做的事,放在工作區即可(縮排就是for
迴圈的工作區)。隨著你學到的語法增加,你會的操作變多了,便會增加你運用for
迴圈的能力。
控制迴圈的工具:break 與 continue 陳述句。
善用break
與continue
可以讓迴圈執行更多功能,也可以避免產生無限迴圈。
使用break 陳述句跳出迴圈
迴圈執行的過程中,如果碰到break
陳述句就會立刻跳出迴圈。
我們用前面提到的,串列與for
迴圈段落的早餐(breakfast)例子,來說明 break
陳述句。
情境是這樣的:
吃早餐的時候,你會以吃蘋果作為最後一個食物,只要吃到蘋果(apple),你就不再吃其他東西。(感覺是個不錯的減肥方式)
下面的例子中,我們在for
迴圈中加入if
陳述句,來判斷時不是吃到蘋果了,如果吃到蘋果就跳出for
迴圈。
>>>breakfast = ['egg', 'milk', 'apple', 'sandwich']
>>>for food in breakfast:
>>> if food == 'apple':
>>> print(food)
>>> break
>>> print(food)
執行程式後,果然吃到蘋果(apple)便停止了,後面的三明治(sandwich)沒有再吃下去。
egg
milk
apple
使用else陳述句檢查break是否被呼叫
如果你想確認break
陳述句有沒有被呼叫,可以使用else
陳述句來檢查。
你可能會覺得else
不就是該跟if
一同使用嗎?怎麼用到for
迴圈來了,雖然for
與else
的合作看起來有點怪,但它們是可以合作的。
怎麼檢查法呢?如果for
迴圈有執行if判斷式中的break
,else
便不會執行;相反的,如果break
沒有被呼叫執行,else
便會執行。有點像有我就沒有它的感覺,執行break
就不會在乎叫else
。
新的情境是這樣的,你希望早餐喝到汽水,喝到汽水你就停止進食:
>>>breakfast = ['egg', 'milk', 'apple', 'sandwich']
>>>for food in breakfast:
>>> if food == 'soda':
>>> print(food)
>>> break
>>> print(food)
>>>else:
>>> print('No soda in breakfast!')
如果for
迴圈在早餐中,遊歷到'soda'
字串,break
被執行,迴圈停止,else
不會執行。
而當for
迴圈在早餐中,沒見到到'soda'
,break
沒有被呼叫執行,else
就會被執行,跳出來說:早餐沒有汽水啦!
程式碼執行結果:
egg
milk
apple
sandwich
No soda in breakfast!
上方程式碼的結果,顯示早餐你吃了egg、milk、apple、sandwich,但根本沒有汽水soda,break
沒有執行,所以else
執行後印出No soda in breakfast!
使用continue 陳述句跳到下一圈
continue的英文意思是繼續,在 Python 世界裡,continue
是停止執行下方的程式碼,跳到迴圈的開頭,再繼續執行迴圈。
continue
陳述句的用法比 break
陳述句複雜一些,它包含了兩個動作:
- 遇到 continue 就停住,不執行其後的程式碼。
- 跳回到迴圈的開頭,再繼續執行程式碼。
同樣的,我們用早餐(breakfast)例子來說明 continue
陳述句,不過這次的狀況是,你其實有乳糖不耐症,所以你不喝牛奶(milk),然後你決定每次都要把家人用心準備的早餐吃完,除非家人準備了牛奶,你無法喝牛奶。
>>>breakfast = ['egg', 'milk', 'apple', 'sandwich']
>>>for food in breakfast:
>>> if food == 'milk':
>>> continue
>>> print(food)
當食物是牛奶的時候,會遇到continue
陳述句,便立刻回到迴圈的起始處,繼續迭代下一個項目。
程式碼執行後,回傳的結果:
egg
apple
sandwich
利用 Python Tutor 的程式碼視覺化,你可以很清楚的了解程式碼是怎麼跑的,遇到continue
陳述句馬上跳到迴圈起始處是怎麼一回事。
希望本文對於for
迴圈的說明可以對你有幫助,如果你碰到迴圈還是覺得頭眼昏花,《Python 自動化的樂趣(第2版)》的第二章以流程圖說明迴圈,並提供了許多練習及詳細的講解,推薦你參考這本書,以實作的方式,多寫幾次for
迴圈的程式碼,就會開始懂了。
送給你歌手孫燕姿的開始懂了,聽完歌也許就懂了。
延伸閱讀: