天天看點

《Python程式設計快速上手——讓繁瑣工作自動化》——2.7 控制流語句

本節書摘來自異步社群《python程式設計快速上手——讓繁瑣工作自動化》一書中的第2章,第2.7節,作者[美] al sweigart,王海鵬 譯,更多章節内容可以通路雲栖社群“異步社群”公衆号檢視。

現在,讓我們來看最重要的控制流部分:語句本身。語句代表了在圖2-1的流程圖中看到的菱形,它們是程式将做出的實際決定。

最常見的控制流語句是if語句。if語句的子句(也就是緊跟if語句的語句塊),将在語句的條件為true時執行。如果條件為false,子句将跳過。

在英文中,if語句念起來可能是:“如果條件為真,執行子句中的代碼。”在python中,if語句包含以下部分:

if關鍵字;

條件(即求值為true或false的表達式);

冒号;

在下一行開始,縮進的代碼塊(稱為if子句)。

例如,假定有一些代碼,檢查某人的名字是否為alice(假設此前曾為name指派)。

所有控制流語句都以冒号結尾,後面跟着一個新的代碼塊(子句)。語句的if子句是代碼塊,包含print('hi, alice.')。圖2-3展示了這段代碼的流程圖。

《Python程式設計快速上手——讓繁瑣工作自動化》——2.7 控制流語句

if子句後面有時候也可以跟着else語句。隻有if語句的條件為false時,else子句才會執行。在英語中,else語句讀起來可能是:“如果條件為真,執行這段代碼。否則,執行那段代碼”。else語句不包含條件,在代碼中,else語句中包含下面部分:

else關鍵字;

在下一行開始,縮進的代碼塊(稱為else子句)。

回到alice的例子,我們來看看使用else語句的一些代碼,在名字不是alice時,提供不一樣的問候。

圖2-4展示了這段代碼的流程圖。

《Python程式設計快速上手——讓繁瑣工作自動化》——2.7 控制流語句

雖然隻有if或else子句會被執行,但有時候可能你希望,“許多”可能的子句中有一個被執行。elif語句是“否則如果”,總是跟在if或另一條elif語句後面。它提供了另一個條件,僅在前面的條件為false時才檢查該條件。在代碼中,elif語句總是包含以下部分:

elif關鍵字;

在下一行開始,縮進的代碼塊(稱為elif子句)。

讓我們在名字檢查程式中添加elif,看看這個語句的效果。

這一次,檢查此人的年齡。如果比 12 歲小,就告訴他一些不同的東西。可以在圖2-5中看到這段代碼的流程圖。

如果age < 12為true并且name == 'alice'為false,elif子句就會執行。但是,如果兩個條件都為false,那麼兩個子句都會跳過。“不能”保證至少有一個子句會被執行。如果有一系列的elif語句,僅有一條或零條子句會被執行。一旦一個語句的條件為true,剩下的elif子句會自動跳過。例如,打開一個新的檔案編輯器視窗,輸入以下代碼,儲存為vampire.py。

《Python程式設計快速上手——讓繁瑣工作自動化》——2.7 控制流語句

這裡,我添加了另外兩條elif語句,讓名字檢查程式根據age的不同答案而發出問候。圖2-6展示了這段代碼的流程圖。

但是,elif語句的次序确實重要。讓我們重新排序,引入一個缺陷。回憶一下,一旦找到一個true條件,剩餘的子句就會自動跳過。是以如果交換vampire.py中的一些子句,就會遇到問題。像下面這樣改變代碼,将它儲存為vampire2.py。

《Python程式設計快速上手——讓繁瑣工作自動化》——2.7 控制流語句

假設在這段代碼執行之前,age變量的值是3000。你可能預計代碼會列印出字元串'unlike you, alice is not an undead, immortal vampire.'。但是,因為age > 100條件為真(畢竟3000大于100)1,字元串'you are not alice, grannie.'被列印出來,剩下的語句自動跳過。别忘了,最多隻有一個子句會執行,對于elif語句,次序是很重要的。

圖2-7展示了前面代碼的流程圖。請注意,菱形age > 100和age > 2000交換了位置。

《Python程式設計快速上手——讓繁瑣工作自動化》——2.7 控制流語句

你可以選擇在最後的elif語句後面加上else語句。在這種情況下,保證至少一個子句(且隻有一個)會執行。如果每個if和elif語句中的條件都為false,就執行else子句。例如,讓我們使用if、elif和else子句重新編寫alicee程式。

圖2-8展示了這段新代碼的流程圖,我們将它儲存為littlekid.py。

《Python程式設計快速上手——讓繁瑣工作自動化》——2.7 控制流語句

在英語中,這類控制流結構會使得:“如果第一個條件為真,做這個。否則,如果第二個條件為真,做那個。否則,做另外的事。”如果你同時使用這3個語句,要記住這些次序規則,避免圖2-7中那樣的缺陷。首先,總是隻有一個if語句。所有需要的elif語句都應該跟在if語句之後。其次,如果希望確定至少一條子句被執行,在最後加上else語句。

利用while語句,可以讓一個代碼塊一遍又一遍的執行。隻要while語句的條件為true,while子句中的代碼就會執行。在代碼中,while語句總是包含下面幾部分:

關鍵字;

條件(求值為true或false的表達式);

從新行開始,縮進的代碼塊(稱為while子句)。

可以看到,while語句看起來和if語句類似。不同之處是它們的行為。if子句結束時,程式繼續執行if語句之後的語句。但在while子句結束時,程式執行跳回到while語句開始處。while子句常被稱為“while循環”,或就是“循環”。

讓我們來看一個if語句和一個while循環。它們使用同樣的條件,并基于該條件做出同樣的動作。下面是if語句的代碼:

下面是while語句的代碼:

這些語句類似,if和while都檢查spam的值,如果它小于5,就列印一條消息。但如果運作這兩段代碼,它們各自的表現非常不同。對于if語句,輸出就是"hello, world."。但對于while語句,輸出是"hello, world."重複了5次!看一看這兩段代碼的流程圖,圖2-9和2-10,找一找原因。

《Python程式設計快速上手——讓繁瑣工作自動化》——2.7 控制流語句
《Python程式設計快速上手——讓繁瑣工作自動化》——2.7 控制流語句

帶有if語句的代碼檢查條件,如果條件為true,就列印一次"hello, world."。帶有while循環的代碼則不同,會列印5次。列印5次後停下來是因為,在每次循環疊代末尾,spam中的整數都增加1。這意味着循環将執行5次,然後spam < 5變為false。

在while循環中,條件總是在每次“疊代”開始時檢查(也就是每次循環執行時)。如果條件為true,子句就會執行,然後,再次檢查條件。當條件第一次為false時,while子句就跳過。

這裡有一個小例子,它不停地要求你輸入“your name”(就是這個字元串,而不是你的名字)。選擇filenew window,打開一個新的檔案編輯器視窗,輸入以下代碼,将檔案儲存為yourname.py:

首先,程式将變量name1設定為一個空字元串。這樣,條件name != 'your name'就會求值為true,程式就會進入while循環的子句2。

這個子句内的代碼要求使用者輸入他們的名字,然後賦給name變量3。因為這是語句塊的最後一行,是以執行就回到while循環的開始,重新對條件求值。如果name中的值“不等于”字元串'your name',那麼條件就為true,執行将再次進入while子句。

但如果使用者輸入your name,while循環的條件就變成'your name' != 'your name',它求值為false。條件現在是false,程式就不會再次進入while循環子句,而是跳過它,繼續執行程式後面的部分4。圖2-11展示了yourname.py程式的流程圖。

《Python程式設計快速上手——讓繁瑣工作自動化》——2.7 控制流語句

現在,讓我們來看看yourname.py程式的效果。按f5鍵運作它,輸幾次your name之外的東西,然後再提供程式想要的輸入。

如果永不輸入your name,那麼循環的條件就永遠為false,程式将永遠問下去。這裡,input()調用讓使用者輸入正确的字元串,以便讓程式繼續。在其他程式,條件可能永遠沒有實際變化,這可能會出問題。讓我們來看看如何跳出循環。

有一個捷徑,讓執行提前跳出while循環子句。如果執行遇到break語句,就會馬上退出while循環子句。在代碼中,break語句僅包含break關鍵字。

非常簡單,對嗎?這裡有一個程式,和前面的程式做一樣的事情,但使用了break語句來跳出循環。輸入以下代碼,将檔案儲存為yourname2.py:

第一行1建立了一個“無限循環”,它是一個條件總是為true的while循環。(表達式true總是求值為true。)程式執行将總是進入循環,隻有遇到break語句執行時才會退出(“永遠不”退出的無限循環是一個常見的程式設計缺陷)。

像以前一樣,程式要求使用者輸入your name2。但是現在,雖然執行仍然在while循環内,但有一個if語句會被執行3,檢查name是否等于your name。如果條件為true,break語句就會運作4,執行就會跳出循環,轉到print('thank you!') 5。否則,包含break語句的if語句子句就會跳過,讓執行到達while循環的末尾。此時,程式執行跳回到while語句的開始1,重新檢查條件。因為條件是true,是以執行進入循環,再次要求使用者輸入your name。這個程式的流程圖參見圖2-12。

運作yourname2.py,輸入你為yourname.py程式輸入的同樣文本。重寫的程式應該和原來的程式反應相同。

《Python程式設計快速上手——讓繁瑣工作自動化》——2.7 控制流語句

像break語句一樣,continue語句用于循環内部。如果程式執行遇到continue語句,就會馬上跳回到循環開始處,重新對循環條件求值(這也是執行到達循環末尾時發生的事情)。

讓我們用continue寫一個程式,要求輸入名字和密碼。在一個新的檔案編輯視窗中輸入以下代碼,将程式儲存為swordfish.py。

如果使用者輸入的名字不是joe1,continue語句2将導緻程式執行跳回到循環開始處。再次對條件求值時,執行總是進入循環,因為條件就是true。如果執行通過了if語句,使用者就被要求輸入密碼3。如果輸入的密碼是swordfish,break語句運作4,執行跳出while循環,列印access granted5。否則,執行繼續到while循環的末尾,又跳回到循環的開始。這個程式的流程圖參見圖2-13。

陷在無限循環中?

運作這個程式,提供一些輸入。隻有你聲稱是joe,它才會要求輸入密碼。一旦輸入了正确的密碼,它就會退出。

《Python程式設計快速上手——讓繁瑣工作自動化》——2.7 控制流語句

在條件為true時,while循環就會繼續循環(這是它的名稱的由來)。但如果你想讓一個代碼塊執行固定次數,該怎麼辦?可以通過for循環語句和range()函數來實作。

“類真”和“類假”的值

在代碼中,for語句看起來像for i in range(5):這樣,總是包含以下部分:

for關鍵字;

一個變量名;

in關鍵字;

調用range()方法,最多傳入3個參數;

從下一行開始,縮退的代碼塊(稱為for子句)。

讓我們建立一個新的程式,名為fivetimes.py,看看for循環的效果。

for循環子句中的代碼運作了5次。第一次運作時,變量i被設為0。子句中的print()調用将列印出jimmy five times (0)。python完成for循環子句内所有代碼的一次疊代之後,執行将回到循環的頂部,for語句讓i增加1。這就是為什麼range(5)導緻子句的5次疊代,i分别被設定為0、1、2、3、4。變量i将遞增到(但不包括)傳遞給range()函數的整數。圖2-14展示了fivetimes.py程式的流程圖。

《Python程式設計快速上手——讓繁瑣工作自動化》——2.7 控制流語句

運作這個程式時,它将列印5次jimmy five times和i的值,然後離開for循環。

也可以在循環中使用continue語句。continue語句将讓for循環變量繼續下一個值,就像程式執行已經到達循環的末尾并傳回開始一樣。實際上,隻能在while和for循環内部使用continue和break語句。如果試圖在别處使用這些語句,python将報錯。

作為for循環的另一個例子,請考慮數學家高斯的故事。當高斯還是一個小孩時,老師想給全班同學布置很多計算作業。老師讓他們從0加到100。高斯想到了一個聰明辦法,在幾秒鐘内算出了答案,但你可以用for循環寫一個python程式,替你完成計算。

結果應該是5050。程式剛開始時,total變量被設為0。然後for循環執行100次total = total + num。當循環完成100次疊代時,0到100的每個整數都加給了total。這時,total被列印到螢幕上。即使在最慢的計算機上,這個程式也不用1秒鐘就能完成計算。

(小高斯想到,有50對數加起來是100:1 + 99, 2 + 98, 3 + 97……直到49 + 51。因為50 × 100 是5000,再加上中間的50,是以0到100的所有數之和是5050。聰明的孩子!)

實際上可以用while循環來做和for循環同樣的事,for循環隻是更簡潔。讓我們用與for循環等價的while循環,重寫fivetimes.py。

運作這個程式,輸出應該和使用for循環的fivetimes.py程式一樣。

某些函數可以用多個參數調用,參數之間用逗号分開,range()就是其中之一。這讓你能夠改變傳遞給range()的整數,實作各種整數序列,包括從0以外的值開始。

第一個參數是for循環變量開始的值,第二個參數是上限,但不包含它,也就是循環停止的數字。

range()函數也可以有第三個參數。前兩個參數分别是起始值和終止值,第三個參數是“步長”。步長是每次疊代後循環變量增加的值。

是以調用range(0, 10, 2)将從0數到8,間隔為2。

在為for循環生成序列資料方面,range()函數很靈活。舉例來說,甚至可以用負數作為步長參數,讓循環計數逐漸減少,而不是增加。

運作一個for循環,用range(5, -1, -1)來列印i,結果将從5降至0。