原文:https://automatetheboringstuff.com/2e/chapter2/
所以,你知道單個指令的基本原理,程序就是一系列指令。但是編程的真正優(yōu)勢不僅僅是像周末跑腿一樣一個接一個地運行指令。根據(jù)表達式的求值方式,程序可以決定跳過指令,重復指令,或者從幾條指令中選擇一條來運行。事實上,你幾乎從來不希望你的程序從第一行代碼開始,簡單地執(zhí)行每一行,一直到最后。流程控制語句可以決定在什么條件下執(zhí)行哪些 Python 指令。
這些流程控制語句直接對應于流程圖中的符號,所以我將提供本章中討論的代碼的流程圖版本。圖 2-1 顯示了下雨時該做什么的流程圖。沿著箭頭所指的路線從頭到尾走。
圖 2-1:告訴你如果下雨該怎么做的流程圖
在一個流程圖中,從起點到終點通常有不止一條路線。計算機程序中的代碼行也是如此。流程圖用菱形表示這些分支點,而其他步驟用矩形表示。開始和結(jié)束步驟用圓角矩形表示。
但是在學習流程控制語句之前,你首先需要學習如何表示那些是和否選項,并且你需要理解如何將那些分支點寫成 Python 代碼。為此,讓我們研究一下布爾值、比較運算符和布爾運算符。
布爾值
整數(shù)、浮點和字符串數(shù)據(jù)類型有無限多的可能值,而布爾數(shù)據(jù)類型只有兩個值:True
和False
。(Boolean
是大寫的,因為數(shù)據(jù)類型是以數(shù)學家喬治·布爾的名字命名的。)當作為 Python 代碼輸入時,布爾值True
和False
缺少放在字符串兩邊的引號,它們總是以大寫字母T
或F
開頭,單詞的其余部分是小寫的。在交互式 Shell 中輸入以下內(nèi)容。(其中一些指令是故意不正確的,它們會導致錯誤消息出現(xiàn)。)
>>> spam = True # ?
>>> spam
True
>>> true # ?
Traceback (most recent call last):
File "<pyshell#2>", line 1, in <module>
true
NameError: name 'true' is not defined
>>> True = 2 + 2 # ?
SyntaxError: can't assign to keyword
像任何其他值一樣,布爾值在表達式中使用,并且可以存儲在變量 ? 中。如果你沒有使用合適的大小寫 ? 或者你試圖使用True
和False
作為變量名 ?,Python 會給你一個錯誤信息。
比較運算符
比較運算符,也稱為關(guān)系運算符,比較兩個值并向下求值為一個布爾值。表 2-1 列出了比較運算符。
表 2-1: 比較運算符
運算符 | 含義 |
---|---|
== |
等于 |
!= |
不等于 |
< |
小于 |
> |
大于 |
<= |
小于或等于 |
>= |
大于或等于 |
這些運算符根據(jù)您給它們的值計算為True
或False
。現(xiàn)在讓我們嘗試一些操作符,從==
和!=
開始。
>>> 42 == 42
True
>>> 42 == 99
False
>>> 2 != 3
True
>>> 2 != 2
False
如您所料,當兩邊的值相同時,==
(等于)計算為True
,當兩個值不同時,!=
(不等于)計算為True
。==
和!=
操作符實際上可以處理任何數(shù)據(jù)類型的值。
>>> 'hello' == 'hello'
True
>>> 'hello' == 'Hello'
False
>>> 'dog' != 'cat'
True
>>> True == True
True
>>> True != False
True
>>> 42 == 42.0
True
>>> 42 == '42' # ?
False
請注意,整數(shù)或浮點值總是不等于字符串值。表達式42 == '42'
? 的計算結(jié)果為False
,因為 Python 認為整數(shù)42
不同于字符串'42'
。
另一方面,<
、>
、<=
和>=
操作符只能處理整數(shù)和浮點值。
>>> 42 < 100
True
>>> 42 > 100
False
>>> 42 < 42
False
>>> eggCount = 42
>>> eggCount <= 42 # ?
True
>>> myAge = 29
>>> myAge >= 10 # ?
True
==
和=
運算符的區(qū)別
您可能已經(jīng)注意到, == 操作符(等于)有兩個等號,而 = 操作符(賦值)只有一個等號。這兩個運算符很容易混淆。請記住以下幾點:
-
==
運算符(等于)詢問兩個值是否相同。 -
=
操作符(賦值)將右邊的值放入左邊的變量。
為了幫助記住哪個是哪個,請注意 == 運算符(等于)由兩個字符組成,就像!= 運算符(不等于)由兩個字符組成。
你會經(jīng)常使用比較運算符來比較一個變量的值和其他值,就像在eggCount <= 42
?和myAge >= 10
? 的例子中一樣。(畢竟,您可以直接輸入True
,而不是在代碼中輸入'dog' != 'cat'
。)在學習流程控制語句時,您將會看到更多這樣的例子。
布爾運算符
三個布爾運算符(and
、or
和not
)用于比較布爾值。像比較運算符一樣,它們將這些表達式計算為一個布爾值。讓我們詳細研究一下這些操作符,從and
操作符開始。
二元布爾運算符
and
和or
運算符總是采用兩個布爾值(或表達式),所以它們被認為是二元運算符。如果兩個布爾值都是True
,則and
運算符將表達式求值為True
;否則,求值為False
。使用and
在交互式 Shell 中輸入一些表達式,查看它的運行情況。
>>> True and True
True
>>> True and False
False
真值表顯示了布爾運算符的每一個可能的結(jié)果。表 2-2 是and
運算符的真值表。
表 2-2:和運算符真值表
表達式 | 求值為… |
---|---|
True and True |
True |
True and False |
False |
False and True |
False |
False and False |
False |
另一方面,如果兩個布爾值之一為True
,則or
運算符將表達式求值為True
。如果兩者都是False
,則求值為False
。
>>> False or True
True
>>> False or False
False
你可以在or
運算符的真值表中看到它的每一個可能的結(jié)果,如表 2-3 所示。
表 2-3:或運算符真值表
表達式 | 求值為… |
---|---|
True or True |
True |
True or False |
True |
False or True |
True |
False or False |
False |
not
運算符
與and
和or
不同,not
運算符只對一個布爾值(或表達式)進行運算。這使得它成為一元運算符。not
操作符只計算相反的布爾值。
>>> not True
False
>>> not not not not True # ?
True
就像在演講和寫作中使用雙重否定一樣,你可以嵌套not
操作符 ?,盡管在真實的程序中沒有理由這樣做。表 2-4 顯示了not
的真值表。
表 2-4:非運算符真值表
表達式 | 求值為… |
---|---|
not True |
False |
not False |
True |
混合布爾和比較運算符
由于比較運算符的計算結(jié)果為布爾值,因此您可以在帶有布爾運算符的表達式中使用它們。
回想一下,and
、or
和not
操作符被稱為布爾操作符,因為它們總是對布爾值True
和False
進行操作。雖然像4 < 5
這樣的表達式不是布爾值,但它們是計算結(jié)果為布爾值的表達式。嘗試在交互式 Shell 中輸入一些使用比較運算符的布爾表達式。
>>> (4 < 5) and (5 < 6)
True
>>> (4 < 5) and (9 < 6)
False
>>> (1 == 2) or (2 == 2)
True
計算機會先計算左邊的表達式,然后再計算右邊的表達式。當它知道了每一個的布爾值后,它將計算整個表達式的布爾值。你可以把計算機對(4 < 5) and (5 < 6)
的求值過程想象成如下:
您也可以在表達式中使用多個布爾運算符以及比較運算符:
>>> 2 + 2 == 4 and not 2 + 2 == 5 and 2 * 2 == 2 + 2
True
布爾運算符的運算順序與數(shù)學運算符一樣。在任何數(shù)學和比較操作符求值之后,Python 首先求值not
操作符,然后是and
操作符,然后是or
操作符。
流程控制要素
流程控制語句通常以稱為條件的部分開始,后面總是跟著稱為子句的代碼塊。在您了解 Python 的特定流程控制語句之前,我將介紹什么是條件和塊。
條件
到目前為止,你看到的布爾表達式都可以被認為是條件,和表達式是一回事;條件只是流程控制語句上下文中一個更具體的名稱。條件總是向下求值為布爾值,True
或False
。一個流程控制語句根據(jù)它的條件是True
還是False
來決定做什么,幾乎每個流程控制語句都使用一個條件。
代碼塊
Python 代碼行可以在塊中組合在一起。您可以從代碼行的縮進來判斷一個塊何時開始和結(jié)束。塊有三個規(guī)則。
- 當縮進增加時,塊開始。
- 塊可以包含其他塊。
- 當縮進量減少到零或包含塊的縮進量時,塊結(jié)束。
通過查看一些縮進的代碼,塊更容易理解,所以讓我們在一個小游戲程序的一部分中找到塊,如下所示:
name = 'Mary'
password = 'swordfish'
if name == 'Mary':
print('Hello, Mary') # ?
if password == 'swordfish':
print('Access granted.') # ?
else:
print('Wrong password.') # ?
您可以在autbor.com/blocks
查看該程序的執(zhí)行情況。第一塊代碼 ? 從第print('Hello, Mary')
行開始,包含其后的所有行。在這個塊里面是另一個塊 ?,其中只有一行代碼:print('Access Granted.')
。第三塊 ? 也是一行長:print('Wrong password.')
。
程序執(zhí)行
在前一章的hello.py
程序中,Python 開始執(zhí)行程序頂端往下的指令,一個接一個。程序執(zhí)行(或簡稱執(zhí)行)是當前正在執(zhí)行的指令的術(shù)語。如果你把源代碼打印在紙上,并在執(zhí)行的時候把手指放在每一行上,你可以把手指想象成程序的執(zhí)行。
然而,并不是所有的程序都是直接向下執(zhí)行的。如果您用手指跟蹤一個帶有流程控制語句的程序,您可能會發(fā)現(xiàn)自己根據(jù)條件跳過源代碼,并且可能會跳過整個子句。
流程控制語句
現(xiàn)在,讓我們探索流程控制中最重要的部分:語句本身。這些語句代表你在圖 2-1 的流程圖中看到的菱形,它們是你的程序?qū)⒁龀龅膶嶋H決定。
if
語句
最常見的流程控制語句是if
語句。如果語句的條件是True
,那么if
語句的子句(即if
語句后面的塊)將會執(zhí)行。如果條件為False
,則跳過該子句。
簡單地說,if
語句可以理解為,“如果這個條件為真,則執(zhí)行子句中的代碼”。在 Python 中,if
語句由以下內(nèi)容組成:
-
if
關(guān)鍵字 - 條件(即計算結(jié)果為
True
或False
的表達式) - 一個冒號
- 從下一行開始,一個縮進的代碼塊(稱為
if
子句)
例如,假設(shè)你有一些代碼來檢查某人的名字是否是愛麗絲。(假設(shè)name
之前被賦予了某個值。)
if name == 'Alice':
print('Hi, Alice.')
所有流程控制語句都以冒號結(jié)尾,后跟一個新的代碼塊(子句)。這個if
語句的子句是帶有print('Hi, Alice.')
的塊。圖 2-2 顯示了這段代碼的流程圖。
圖 2-2:if
語句的流程圖
if-else
語句
一個if
子句可以選擇跟一個else
語句。只有當if
語句的條件為False
時,才會執(zhí)行else
子句。用簡單的英語來說,else
語句可以理解為,“如果這個條件為真,執(zhí)行這個代碼。否則,執(zhí)行該代碼”。else
語句沒有條件,在代碼中,else
語句總是由以下內(nèi)容組成:
-
else
關(guān)鍵字 - 一個冒號
- 從下一行開始,一個縮進的代碼塊(稱為
else
子句)
回到Alice
的例子,讓我們看一些代碼,如果這個人的名字不是Alice
,這些代碼使用一個else
語句來提供不同的問候。
if name == 'Alice':
print('Hi, Alice.')
else:
print('Hello, stranger.')
圖 2-3 顯示了這段代碼的流程圖。
圖 2-3:一個else
語句的流程圖
elif
語句
雖然只有if
或else
子句中的一個會執(zhí)行,但您可能希望執(zhí)行多個可能子句中的一個。elif
語句是一個else if
語句,總是跟在一個if
或另一個elif
語句之后。它提供了另一個條件,僅當所有之前的條件都為False
時才檢查該條件。在代碼中,elif
語句總是由以下內(nèi)容組成:
-
elif
關(guān)鍵字 - 條件(即計算結(jié)果為
True
或False
的表達式) - 一個冒號
- 從下一行開始,一個縮進的代碼塊(稱為
elif
子句)
讓我們給名稱檢查器添加一個elif
來看看這個語句的運行情況。
if name == 'Alice':
print('Hi, Alice.')
elif age < 12:
print('You are not Alice, kiddo.')
這一次,你檢查這個人的年齡,如果他們小于 12 歲,程序會告訴他們一些不同的東西。你可以在圖 2-4 中看到流程圖。
圖 2-4:一條elif
語句的流程圖
如果age < 12
是True
并且name == 'Alice'
是False
,則elif
子句執(zhí)行。然而,如果兩個條件都是False
,則兩個子句都被跳過。不能保證至少有一個條款會被執(zhí)行。當有一串elif
語句時,只有一個或沒有一個子句會被執(zhí)行。一旦發(fā)現(xiàn)其中一個語句的條件是True
,其余的elif
子句將被自動跳過。例如,打開一個新的文件編輯器窗口,輸入以下代碼,保存為vampire.py
:
name = 'Carol'
age = 3000
if name == 'Alice':
print('Hi, Alice.')
elif age < 12:
print('You are not Alice, kiddo.')
elif age > 2000:
print('Unlike you, Alice is not an undead, immortal vampire.')
elif age > 100:
print('You are not Alice, grannie.')
您可以在autbor.com/vampire
查看該程序的執(zhí)行情況。這里,我又添加了兩個elif
語句,讓姓名檢查器根據(jù)age
用不同的答案問候一個人。圖 2-5 顯示了此操作的流程圖。
圖 2-5:vampire.py
程序中多個elif
語句的流程圖
然而,elif
語句的順序很重要。讓我們重新排列它們來引入一個 bug。請記住,一旦發(fā)現(xiàn)一個True
條件,其余的elif
子句就會被自動跳過,所以如果你調(diào)換了vampire.py
中的一些子句,就會遇到問題。將代碼更改為如下所示,并保存為vampire2.py
:
name = 'Carol'
age = 3000
if name == 'Alice':
print('Hi, Alice.')
elif age < 12:
print('You are not Alice, kiddo.')
elif age > 100: # ?
print('You are not Alice, grannie.')
elif age > 2000:
print('Unlike you, Alice is not an undead, immortal vampire.')
您可以在autbor.com/vampire2
查看該程序的執(zhí)行情況。假設(shè)在執(zhí)行這段代碼之前,age
變量包含值3000
。您可能希望代碼打印出字符串'Unlike you, Alice is not an undead, immortal vampire.'
。但是因為age > 100
條件是True
(畢竟 3000 大于 100)?,所以打印出字符串'You are not Alice, grannie.'
,其余的elif
語句自動跳過。記住,最多只會執(zhí)行一個子句,對于elif
語句,順序很重要!
圖 2-6 顯示了先前代碼的流程圖。請注意age > 100
和age > 2000
的鉆石是如何交換的。
可選地,您可以在最后一個elif
語句之后有一個else
語句。在這種情況下,保證至少有一個(且只有一個)子句會被執(zhí)行。如果每個if
和elif
語句中的條件都是False
,則執(zhí)行else
子句。例如,讓我們重新創(chuàng)建Alice
程序來使用if
、elif
和else
子句。
name = 'Carol'
age = 3000
if name == 'Alice':
print('Hi, Alice.')
elif age < 12:
print('You are not Alice, kiddo.')
else:
print('You are neither Alice nor a little kid.')
您可以在autbor.com/littlekid
查看該程序的執(zhí)行情況。圖 2-7 顯示了這個新代碼的流程圖,我們將它保存為littleKid.py
。
簡單地說,這種類型的流程控制結(jié)構(gòu)應該是“如果第一個條件為真,就這樣做。否則,如果第二個條件為真,就這樣做。否則,做點別的”。當你一起使用if
、elif
和else
語句時,記住這些關(guān)于如何排序的規(guī)則,以避免類似圖 2-6 中的錯誤。首先,總是恰好有一個if
語句。您需要的任何elif
語句都應該遵循if
語句。第二,如果您想確保至少有一個子句被執(zhí)行,那么用一個else
語句來關(guān)閉這個結(jié)構(gòu)。
圖 2-6:vampire2.py
程序的流程圖。X
路徑在邏輯上永遠不會發(fā)生,因為如果年齡大于 2000 ,它就已經(jīng)大于 100 。
圖 2-7:之前littleKid.py
程序的流程圖
####while
循環(huán)語句
您可以使用while
語句讓一段代碼反復執(zhí)行。只要while
語句的條件為True
,就會執(zhí)行while
子句中的代碼。在代碼中,while
語句總是由以下內(nèi)容組成:
-
while
關(guān)鍵字 - 條件(即計算結(jié)果為
True
或False
的表達式) - 一個冒號
- 從下一行開始,一個縮進的代碼塊(稱為
while
子句)
你可以看到一個while
語句看起來類似于一個if
語句。區(qū)別在于他們的行為方式。在一個if
子句的末尾,程序在if
語句之后繼續(xù)執(zhí)行。但是在一個while
子句的末尾,程序執(zhí)行會跳回到while
語句的開頭。while
子句通常被稱為*while
循環(huán)或簡稱為循環(huán)*。
讓我們看一下使用相同條件并基于該條件采取相同動作的if
語句和while
循環(huán)。下面是帶有if
語句的代碼:
spam = 0
if spam < 5:
print('Hello, world.')
spam = spam + 1
下面是帶有while
語句的代碼:
spam = 0
while spam < 5:
print('Hello, world.')
spam = spam + 1
這些語句是相似的——if
和while
都檢查spam
的值,如果小于 5,它們就打印一條消息。但是當您運行這兩個代碼片段時,每一個都會發(fā)生非常不同的事情。對于if
語句,輸出只是"Hello, world."
。但是對于while
語句,是"Hello, world."
重復了五次!看看這兩段代碼的流程圖,圖 2-8 和 2-9 ,看看為什么會這樣。
圖 2-8:if
語句代碼的流程圖
圖 2-9:while
語句代碼的流程圖
帶有if
語句的代碼檢查條件,如果條件為真,它只打印一次Hello, world.
。另一方面,帶有while
循環(huán)的代碼將打印五次。五次打印后循環(huán)停止,因為spam
中的整數(shù)在每次循環(huán)迭代結(jié)束時增加 1,這意味著在spam < 5
為False
之前循環(huán)將執(zhí)行五次。
在while
循環(huán)中,總是在每次迭代開始時檢查條件(即每次循環(huán)執(zhí)行時)。如果條件為True
,則執(zhí)行該子句,然后再次檢查條件。第一次發(fā)現(xiàn)條件為False
,跳過while
子句。
煩人的while
循環(huán)
這里有一個小的示例程序,它會一直要求你鍵入,字面意思是,your name
。選擇文件 -> 新建打開一個新的文件編輯器窗口,輸入以下代碼,將文件保存為yourName.py
:
name = '' # ?
while name != 'your name': # ?
print('Please type your name.')
name = input() # ?
print('Thank you!') # ?
您可以在autbor.com/yourname
查看該程序的執(zhí)行情況。首先,程序?qū)⒆兞?code>name?設(shè)置為一個空字符串。這是如此的以至于name != 'your name'
條件將求值為True
并且程序執(zhí)行將進入while
循環(huán)的子句 ?。
這個子句中的代碼要求用戶鍵入他們的名字,這個名字被分配給name
變量 ?。因為這是塊的最后一行,所以執(zhí)行返回到while
循環(huán)的開始,并重新求值條件。如果name
中的值不等于字符串'your name'
,則條件為True
,執(zhí)行再次進入while
子句。
但是一旦用戶輸入your name
,while
循環(huán)的條件將是'your name' != 'your name'
,其值為False
?,F(xiàn)在條件是False
,而不是程序執(zhí)行重新進入while
循環(huán)的子句,Python 跳過它并繼續(xù)運行程序的剩余部分 ?。圖 2-10 顯示了yourName.py
程序的流程圖。
圖 2-10:yourname.py
程序的流程圖
現(xiàn)在,讓我們來看看yourName.py
的運行情況。按下F5
來運行它,在你給程序它想要的東西之前,輸入幾次your name
以外的東西。
Please type your name.
Al
Please type your name.
Albert
Please type your name.
%#@#%*(^&!!!
Please type your name.
your name
Thank you!
如果你從來不輸入your name
,那么while
循環(huán)的條件將永遠不會是False
,程序?qū)恢痹儐栂氯ァ_@里,input()
調(diào)用讓用戶輸入正確的字符串,讓程序繼續(xù)運行。在其他程序中,條件可能永遠不會真正改變,這可能是一個問題。讓我們看看如何打破while
循環(huán)。
break
語句
讓程序執(zhí)行盡早脫離while
循環(huán)的子句有一個捷徑。如果執(zhí)行到了一個break
語句,它會立即退出while
循環(huán)的子句。在代碼中,break
語句只包含break
關(guān)鍵字。
很簡單,對吧?這里有一個程序,它和前面的程序做同樣的事情,但是它使用了一個break
語句來避免循環(huán)。輸入以下代碼,并將文件保存為yourName2.py
:
while True: # ?
print('Please type your name.')
name = input() # ?
if name == 'your name': # ?
break # ?
print('Thank you!') # ?
您可以在autbor.com/yourname2
查看該程序的執(zhí)行情況。第一行 ? 創(chuàng)建無限循環(huán);這是一個條件始終為True
的while
循環(huán)。(畢竟,表達式True
的值總是小于值True
。)程序執(zhí)行進入這個循環(huán)后,只有執(zhí)行了一條break
語句才會退出循環(huán)。(永遠不會退出的無限循環(huán)是常見的編程錯誤。)
就像之前一樣,這個程序要求用戶輸入your name
?。然而,現(xiàn)在當執(zhí)行仍在while
循環(huán)中時,一個if
語句檢查 ? 是否等于'your name'
。如果該條件為True
,則break
語句運行 ?,執(zhí)行移出循環(huán)至print('Thank you!')
?。否則,包含break
語句的if
語句的子句被跳過,這將執(zhí)行放在while
循環(huán)的末尾。此時,程序執(zhí)行跳回到while
語句的開始處 ? 以重新檢查條件。由于這個條件僅僅是布爾值True
,執(zhí)行進入循環(huán),要求用戶再次輸入your name
。該程序的流程圖見圖 2-11 。
運行yourName2.py
,輸入您為yourName.py
輸入的相同文本。重寫的程序應該以與原始程序相同的方式響應。
圖 2-11:無限循環(huán)的yourName2.py
程序的流程圖。注意,X 路徑在邏輯上永遠不會發(fā)生,因為循環(huán)條件總是真。
continue
語句
像break
語句一樣,continue
語句也在循環(huán)中使用。當程序執(zhí)行到一個continue
語句時,程序執(zhí)行立即跳回到循環(huán)的開始,并重新求值循環(huán)的條件。(這也是執(zhí)行到循環(huán)末尾時發(fā)生的情況。)
讓我們用continue
寫一個要求輸入名字和密碼的程序。在新的文件編輯器窗口中輸入以下代碼,并將程序保存為swordfish.py
。
陷入無限循環(huán)?
如果你曾經(jīng)運行過一個程序,它有一個錯誤導致它陷入無限循環(huán),按下CTRL+C
或者從 IDLE 的菜單中選擇Shell -> 重啟 Shell 。這將向你的程序發(fā)送一個鍵盤中斷錯誤,并導致它立即停止。嘗試通過在文件編輯器中創(chuàng)建一個簡單的無限循環(huán)來停止程序,并將程序保存為infiniteLoop.py
。
while True:
print('Hello, world!')
當你運行這個程序時,它會打印出'Hello, world!'
,永遠顯示在屏幕上,因為while
語句的條件總是為真。如果你想簡單地立即終止你的程序,CTRL+C
也很方便,即使它沒有陷入無限循環(huán)。
while True:
print('Who are you?')
name = input()
if name != 'Joe': # ?
continue # ?
print('Hello, Joe. What is the password? (It is a fish.)')
password = input() # ?
if password == 'swordfish':
break # ?
print('Access granted.') # ?
如果用戶輸入除了Joe
? 之外的任何名字,則continue
語句 ? 使程序執(zhí)行跳回到循環(huán)的開始。當程序重新求值條件時,執(zhí)行將總是進入循環(huán),因為條件僅僅是值True
。一旦用戶通過了那個if
語句,他們就會被要求輸入密碼 ?。如果輸入的密碼是swordfish
,則運行break
語句 ?,執(zhí)行跳出while
循環(huán)打印Access granted
?。否則,執(zhí)行繼續(xù)到while
循環(huán)的結(jié)尾,然后跳回到循環(huán)的開始。該程序的流程圖見圖 2-12 。
圖 2-12:swordFish.py
。X
路徑邏輯上永遠不會發(fā)生,因為循環(huán)條件總是真。
“真”和“假”值
條件將考慮其他數(shù)據(jù)類型中的一些值,相當于真和假。在條件中使用時,0,0.0,''
(空字符串)被認為是假,而所有其他值被認為是真。例如,看看下面的程序:
name = ''
while not name: # ?
print('Enter your name:')
name = input()
print('How many guests will you have?')
numOfGuests = int(input())
if numOfGuests: # ?
print('Be sure to have enough room for all your guests.') # ?
print('Done')
您可以在autbor.com/howmanyguests
查看該程序的執(zhí)行情況。如果用戶為名稱輸入一個空字符串,那么while
語句的條件將為True
?,程序繼續(xù)詢問名稱。如果numOfGuests
的值不是 0 ?,則認為該條件為真,程序?qū)橛脩?? 打印一個提醒。
你可以輸入not name != ''
代替not name
,輸入numOfGuests != 0
代替numOfGuests
,但是使用真值和假值可以使您的代碼更容易閱讀。
運行這個程序,給它一些輸入。在你自稱是喬之前,這個程序不應該要求輸入密碼,一旦你輸入了正確的密碼,它就應該退出。
Who are you?
I'm fine, thanks. Who are you?
Who are you?
Joe
Hello, Joe. What is the password? (It is a fish.)
Mary
Who are you?
Joe
Hello, Joe. What is the password? (It is a fish.)
swordfish
Access granted.
您可以在autbor.com/hellojoe
查看該程序的執(zhí)行情況。
while
循環(huán)和range()
函數(shù)
while
循環(huán)在它的條件為True
時保持循環(huán)(這就是它的名字的原因),但是如果你只想執(zhí)行一段代碼一定的次數(shù)呢?你可以用一個for
循環(huán)語句和range()
函數(shù)來實現(xiàn)。
在代碼中,for
語句類似于for i in range(5):
,包括以下內(nèi)容:
-
for
關(guān)鍵字 - 變量名
-
in
關(guān)鍵字 - 調(diào)用
range()
方法,最多傳遞三個整數(shù) - 一個冒號
- 從下一行開始,一個縮進的代碼塊(稱為
for
子句)
讓我們創(chuàng)建一個名為fiveTimes.py
的新程序來幫助您查看運行中的for
循環(huán)。
print('My name is')
for i in range(5):
print('Jimmy Five Times (' + str(i) + ')')
您可以在autbor.com/fivetimesfor
](https://autbor.com/fivetimesfor/)查看該程序的執(zhí)行情況。循環(huán)的子句中的代碼運行了五次。第一次運行時,變量i
被設(shè)置為0
。子句中的print()
調(diào)用將打印Jimmy Five Times (0)
。在 Python 完成了對for
循環(huán)的子句中所有代碼的迭代之后,執(zhí)行返回到循環(huán)的頂部,并且for
語句將i
加 1。這就是為什么range(5)
在子句中導致五次迭代,其中i
被設(shè)置為0
,然后是1
,然后是2
,然后是3
,然后是4
。變量i
將上升到傳遞給range()
的整數(shù),但不包括該整數(shù)。圖 2-13 顯示了fiveTimes.py
程序的流程圖。
當你運行這個程序時,在離開for
循環(huán)之前,它應該打印五次Jimmy Five Times
,然后是i
的值。
My name is
Jimmy Five Times (0)
Jimmy Five Times (1)
Jimmy Five Times (2)
Jimmy Five Times (3)
Jimmy Five Times (4)
注
對于循環(huán),也可以使用中的
break
和continue
語句。continue
語句將繼續(xù)到循環(huán)的計數(shù)器的的下一個值,就好像程序執(zhí)行已經(jīng)到達循環(huán)的結(jié)尾并返回到開始。事實上,您只能在中使用break
和continue
語句,而和用于循環(huán)。如果你試圖在其他地方使用這些語句,Python 會給你一個錯誤。
圖 2-13:fiveTimes.py
的流程圖
作為另一個循環(huán)例子,考慮這個關(guān)于數(shù)學家卡爾·弗里德里希·高斯的故事。高斯小的時候,一位老師想給全班同學一些作業(yè)。老師告訴他們把從 0 到 100 的所有數(shù)字加起來。年輕的高斯想出了一個聰明的辦法,在幾秒鐘內(nèi)算出答案,但是你可以寫一個帶有for
循環(huán)的 Python 程序來替你做這個計算。
total = 0 # ?
for num in range(101): # ?
total = total + num # ?
print(total) # ?
結(jié)果應該是 5050。當程序首次啟動時,total
變量被設(shè)置為0
?。for
循環(huán) ? 然后執(zhí)行total = total + num
? 100 次。到循環(huán)完成所有 100 次迭代時,從0
到100
的每個整數(shù)都將被加到total
。此時,total
被打印到屏幕 ? 上。即使在最慢的計算機上,這個程序也不到一秒鐘就能完成。
(年輕的高斯想出了一個秒解的方法。有 50 對數(shù)字加起來是 101:1 + 100
,2 + 99
,3 + 98
,以此類推,直到50 + 51
。由于50 × 101
是 5050,所以從 0 到 100 的所有數(shù)之和是 5050。聰明的孩子?。?/p>
一個等價的while
循環(huán)
您實際上可以使用一個while
循環(huán)來做與一個for
循環(huán)相同的事情;for
循環(huán)只是更簡潔。讓我們重寫fiveTimes.py
來使用一個與for
循環(huán)等價的while
循環(huán)。
print('My name is')
i = 0
while i < 5:
print('Jimmy Five Times (' + str(i) + ')')
i = i + 1
您可以在autbor.com/fivetimeswhile
查看該程序的執(zhí)行情況。如果運行這個程序,輸出應該與使用了一個for
循環(huán)的fiveTimes.py
程序看起來一樣。
range()
的start
、stop
和step
參數(shù)
有些函數(shù)可以用逗號分隔的多個參數(shù)來調(diào)用,range()
就是其中之一。這允許您更改傳遞給range()
的整數(shù),使其遵循任何整數(shù)序列,包括從非零數(shù)字開始。
for i in range(12, 16):
print(i)
第一個參數(shù)是循環(huán)變量開始的地方,第二個參數(shù)是停止的數(shù)字,但不包括這個數(shù)字。
12
13
14
15
也可以用三個參數(shù)調(diào)用range()
函數(shù)。前兩個參數(shù)將是開始和停止值,第三個參數(shù)將是步長參數(shù)。步長是每次迭代后變量增加的量。
for i in range(0, 10, 2):
print(i)
所以調(diào)用range(0, 10, 2)
會以 2 為間隔從 0 數(shù)到 8。
0
2
4
6
8
range()
函數(shù)為for
循環(huán)產(chǎn)生的數(shù)字序列是靈活的。舉個栗子(我從不為我的雙關(guān)語道歉),你甚至可以為步長參數(shù)使用負數(shù)來使for
循環(huán)遞減計數(shù)而不是遞增計數(shù)。
for i in range(5, -1, -1):
print(i)
這個for
循環(huán)會有以下輸出:
5
4
3
2
1
0
運行一個for
循環(huán)來用range(5, -1, -1)
打印i
應該從 5 到 0 打印。
導入模塊
所有的 Python 程序都可以調(diào)用一組叫做內(nèi)置函數(shù)的基本函數(shù),包括你之前見過的print()
、input()
和len()
函數(shù)。Python 還附帶了一組稱為標準庫的模塊。每個模塊都是一個 Python 程序,包含一組相關(guān)的函數(shù),可以嵌入到您的程序中。例如,math
模塊有數(shù)學相關(guān)的函數(shù),random
模塊有隨機數(shù)相關(guān)的函數(shù),等等。
在使用模塊中的函數(shù)之前,必須用一個import
語句導入模塊。在代碼中,import
語句由以下內(nèi)容組成:
-
import
關(guān)鍵字 - 模塊的名稱
- 可選地,更多的模塊名稱,只要它們由逗號分隔
一旦你導入了一個模塊,你就可以使用該模塊所有的酷函數(shù)。讓我們試一試random
模塊,它將讓我們訪問random.randint()
函數(shù)。
將該代碼輸入文件編輯器,并保存為printRandom.py
:
import random
for i in range(5):
print(random.randint(1, 10))
不要覆蓋模塊名
當你保存你的 Python 腳本時,注意不要給它們起一個 Python 模塊使用的名字,比如random.py
、sys.py
、os.py
或者math.py
。如果您不小心命名了一個程序,比如說,random.py
,并在另一個程序中使用了一個import random
語句,那么您的程序?qū)肽?code>random.py文件,而不是 Python 的random
模塊。這會導致諸如“AttributeError
:模塊random
沒有屬性randint
”的錯誤,因為你的random.py
沒有真正的random
模塊所具有的函數(shù)。也不要使用任何內(nèi)置 Python 函數(shù)的名字,比如print()
或者input()
。
像這樣的問題并不常見,但解決起來卻很棘手。隨著您獲得更多的編程經(jīng)驗,您將會更加了解 Python 的模塊和函數(shù)所使用的標準名稱,并減少遇到這些問題的頻率。
當你運行這個程序時,輸出會是這樣的:
4
1
8
4
1
您可以在autbor.com/printrandom
查看該程序的執(zhí)行情況。random.randint()
函數(shù)調(diào)用的計算結(jié)果是您傳遞給它的兩個整數(shù)之間的隨機整數(shù)值。由于randint()
在random
模塊中,你必須首先鍵入random.
放在函數(shù)名前面,告訴 Python 在random
模塊中尋找這個函數(shù)。
下面是一個import
語句的例子,它導入了四個不同的模塊:
import random, sys, os, math
現(xiàn)在我們可以使用這四個模塊中的任何函數(shù)。我們將在本書的后面了解更多。
from * import
語句
import
語句的另一種形式是由from
關(guān)鍵字、模塊名、import
關(guān)鍵字和一個星號組成;比如from random import *
。
使用這種形式的import
語句,對random
中函數(shù)的調(diào)用將不需要random.
前綴。然而,使用全名有助于提高代碼的可讀性,所以最好使用語句的import random
形式。
用sys.exit()函數(shù)提前結(jié)束程序
最后一個要介紹的流程控制概念是如何終止程序。如果程序執(zhí)行到指令的底部,程序總是終止。然而,您可以通過調(diào)用sys.exit()
函數(shù)使程序在最后一條指令之前終止或退出。因為這個函數(shù)在sys
模塊中,你必須在你的程序使用它之前導入sys
。
打開文件編輯器窗口,輸入以下代碼,保存為exitExample.py
:
import sys
while True:
print('Type exit to exit.')
response = input()
if response == 'exit':
sys.exit()
print('You typed ' + response + '.')
空閑時運行該程序。這個程序有一個內(nèi)部沒有break
語句的無限循環(huán)。這個程序結(jié)束的唯一方式是執(zhí)行到了sys.exit()
調(diào)用。當response
等于exit
時,包含sys.exit()
調(diào)用的行被執(zhí)行。由于response
變量由input()
函數(shù)設(shè)置,用戶必須輸入exit
才能停止程序。
一個小程序:猜數(shù)字
到目前為止,我向您展示的示例對于介紹基本概念是有用的,但是現(xiàn)在讓我們看看您所學的一切是如何在一個更完整的程序中組合在一起的。在這一部分,我將向您展示一個簡單的“猜數(shù)字”游戲。當您運行該程序時,輸出將類似于以下內(nèi)容:
I am thinking of a number between 1 and 20.
Take a guess.
10
Your guess is too low.
Take a guess.
15
Your guess is too low.
Take a guess.
17
Your guess is too high.
Take a guess.
16
Good job! You guessed my number in 4 guesses!
在文件編輯器中輸入下面的源代碼,將文件保存為guessTheNumber.py
:
# This is a guess the number game.
import random
secretNumber = random.randint(1, 20)
print('I am thinking of a number between 1 and 20.')
# Ask the player to guess 6 times.
for guessesTaken in range(1, 7):
print('Take a guess.')
guess = int(input())
if guess < secretNumber:
print('Your guess is too low.')
elif guess > secretNumber:
print('Your guess is too high.')
else:
break # This condition is the correct guess!
if guess == secretNumber:
print('Good job! You guessed my number in ' + str(guessesTaken) + '
guesses!')
else:
print('Nope. The number I was thinking of was ' + str(secretNumber))
您可以在/autbor.com/guessthenumber
查看該程序的執(zhí)行情況。讓我們從頂部開始,逐行查看這段代碼。
# This is a guess the number game.
import random
secretNumber = random.randint(1, 20)
首先,代碼頂部的注釋解釋了程序的功能。然后,程序?qū)?code>random模塊,以便使用random.randint()
函數(shù)生成一個數(shù)字供用戶猜測。返回值是 1 到 20 之間的隨機整數(shù),存儲在變量secretNumber
中。
print('I am thinking of a number between 1 and 20.')
# Ask the player to guess 6 times.
for guessesTaken in range(1, 7):
print('Take a guess.')
guess = int(input())
該程序告訴玩家,它已經(jīng)想出了一個秘密數(shù)字,并會給玩家六次機會來猜測它。讓玩家輸入猜測并檢查猜測是否在最多循環(huán)六次的for
循環(huán)中的代碼。循環(huán)中發(fā)生的第一件事是玩家輸入一個猜測。由于input()
返回一個字符串,它的返回值被直接傳遞給int()
,后者將該字符串翻譯成一個整數(shù)值。這存儲在一個名為guess
的變量中。
if guess < secretNumber:
print('Your guess is too low.')
elif guess > secretNumber:
print('Your guess is too high.')
這幾行代碼檢查猜測值是小于還是大于秘密數(shù)字。在這兩種情況下,都會將提示打印到屏幕上。
else:
break # This condition is the correct guess!
如果猜測既不高于也不低于秘密數(shù)字,那么它必須等于秘密數(shù)字——在這種情況下,您希望程序執(zhí)行脫離for
循環(huán)。
if guess == secretNumber:
print('Good job! You guessed my number in ' + str(guessesTaken) + ' guesses!')
else:
print('Nope. The number I was thinking of was ' + str(secretNumber))
在for
循環(huán)之后,前面的if...else
語句檢查玩家是否猜對了數(shù)字,然后將適當?shù)南⒋蛴〉狡聊簧?。在這兩種情況下,程序都顯示一個包含整數(shù)值的變量(guessesTaken
和secretNumber
)。因為它必須將這些整數(shù)值連接成字符串,所以它將這些變量傳遞給str()
函數(shù),該函數(shù)返回這些整數(shù)的字符串值形式?,F(xiàn)在,在最終傳遞給print()
函數(shù)調(diào)用之前,這些字符串可以用+
操作符連接起來。
一個小程序:石頭、剪刀、布
讓我們使用目前為止所學的編程概念來創(chuàng)建一個簡單的石頭、布、剪刀游戲。輸出將如下所示:
ROCK, PAPER, SCISSORS
0 Wins, 0 Losses, 0 Ties
Enter your move: (r)ock (p)aper (s)cissors or (q)uit
p
PAPER versus...
PAPER
It is a tie!
0 Wins, 1 Losses, 1 Ties
Enter your move: (r)ock (p)aper (s)cissors or (q)uit
s
SCISSORS versus...
PAPER
You win!
1 Wins, 1 Losses, 1 Ties
Enter your move: (r)ock (p)aper (s)cissors or (q)uit
q
在文件編輯器中鍵入以下源代碼,并將文件保存為rpsGame.py
:
import random, sys
print('ROCK, PAPER, SCISSORS')
# These variables keep track of the number of wins, losses, and ties.
wins = 0
losses = 0
ties = 0
while True: # The main game loop.
print('%s Wins, %s Losses, %s Ties' % (wins, losses, ties))
while True: # The player input loop.
print('Enter your move: (r)ock (p)aper (s)cissors or (q)uit')
playerMove = input()
if playerMove == 'q':
sys.exit() # Quit the program.
if playerMove == 'r' or playerMove == 'p' or playerMove == 's':
break # Break out of the player input loop.
print('Type one of r, p, s, or q.')
# Display what the player chose:
if playerMove == 'r':
print('ROCK versus...')
elif playerMove == 'p':
print('PAPER versus...')
elif playerMove == 's':
print('SCISSORS versus...')
# Display what the computer chose:
randomNumber = random.randint(1, 3)
if randomNumber == 1:
computerMove = 'r'
print('ROCK')
elif randomNumber == 2:
computerMove = 'p'
print('PAPER')
elif randomNumber == 3:
computerMove = 's'
print('SCISSORS')
# Display and record the win/loss/tie:
if playerMove == computerMove:
print('It is a tie!')
ties = ties + 1
elif playerMove == 'r' and computerMove == 's':
print('You win!')
wins = wins + 1
elif playerMove == 'p' and computerMove == 'r':
print('You win!')
wins = wins + 1
elif playerMove == 's' and computerMove == 'p':
print('You win!')
wins = wins + 1
elif playerMove == 'r' and computerMove == 'p':
print('You lose!')
losses = losses + 1
elif playerMove == 'p' and computerMove == 's':
print('You lose!')
losses = losses + 1
elif playerMove == 's' and computerMove == 'r':
print('You lose!')
losses = losses + 1
讓我們從頂部開始,逐行查看這段代碼。
import random, sys
print('ROCK, PAPER, SCISSORS')
# These variables keep track of the number of wins, losses, and ties.
wins = 0
losses = 0
ties = 0
首先,我們導入random
和sys
模塊,這樣我們的程序就可以調(diào)用random.randint()
和sys.exit()
函數(shù)。我們還設(shè)置了三個變量來記錄玩家贏了多少,輸了多少,打了多少平手。
while True: # The main game loop.
print('%s Wins, %s Losses, %s Ties' % (wins, losses, ties))
while True: # The player input loop.
print('Enter your move: (r)ock (p)aper (s)cissors or (q)uit')
playerMove = input()
if playerMove == 'q':
sys.exit() # Quit the program.
if playerMove == 'r' or playerMove == 'p' or playerMove == 's':
break # Break out of the player input loop.
print('Type one of r, p, s, or q.')
該程序在另一個while
循環(huán)中使用了一個while
循環(huán)。第一個循環(huán)是主游戲循環(huán),通過這個循環(huán)在每次迭代中玩石頭、布、剪刀的單個游戲。第二個循環(huán)要求玩家輸入,并保持循環(huán),直到玩家為他們的移動輸入了r
、p
、s
或q
。r
、p
和s
分別對應石頭、布、剪刀,而q
表示玩家打算退出。在這種情況下,調(diào)用sys.exit()
,程序退出。如果玩家輸入了r
、p
或s
,執(zhí)行將退出循環(huán)。否則,程序會提醒玩家輸入r
、p
、s
或q
,并返回循環(huán)的起點。
# Display what the player chose:
if playerMove == 'r':
print('ROCK versus...')
elif playerMove == 'p':
print('PAPER versus...')
elif playerMove == 's':
print('SCISSORS versus...')
玩家的移動顯示在屏幕上。
# Display what the computer chose:
randomNumber = random.randint(1, 3)
if randomNumber == 1:
computerMove = 'r'
print('ROCK')
elif randomNumber == 2:
computerMove = 'p'
print('PAPER')
elif randomNumber == 3:
computerMove = 's'
print('SCISSORS')
接下來,隨機選擇計算機的移動。由于random.randint()
只能返回一個隨機數(shù),所以它返回的1
、2
或3
整數(shù)值存儲在一個名為randomNumber
的變量中。程序根據(jù)randomNumber
中的整數(shù)在computerMove
中存儲一個'r'
、'p'
或's'
字符串,并顯示計算機的移動。
# Display and record the win/loss/tie:
if playerMove == computerMove:
print('It is a tie!')
ties = ties + 1
elif playerMove == 'r' and computerMove == 's':
print('You win!')
wins = wins + 1
elif playerMove == 'p' and computerMove == 'r':
print('You win!')
wins = wins + 1
elif playerMove == 's' and computerMove == 'p':
print('You win!')
wins = wins + 1
elif playerMove == 'r' and computerMove == 'p':
print('You lose!')
losses = losses + 1
elif playerMove == 'p' and computerMove == 's':
print('You lose!')
losses = losses + 1
elif playerMove == 's' and computerMove == 'r':
print('You lose!')
losses = losses + 1
最后,程序比較playerMove
和computerMove
中的字符串,并將結(jié)果顯示在屏幕上。它還適當增加wins
、losses
或ties
變量。一旦執(zhí)行到達末尾,它就跳回到主程序循環(huán)的開始,開始另一個游戲。
總結(jié)
通過使用求值為True
或False
(也稱為條件)的表達式,您可以編寫決定執(zhí)行什么代碼和跳過什么代碼的程序。當某個條件求值為True
時,你也可以在一個循環(huán)中反復執(zhí)行代碼。如果你需要退出一個循環(huán)或者跳回到循環(huán)的開始,那么break
和continue
語句是很有用的。
這些流程控制語句將讓您編寫更智能的程序。您也可以通過編寫自己的函數(shù)來使用另一種類型的流程控制,這是下一章的主題。
練習題
-
布爾數(shù)據(jù)類型的兩個值是什么?你是怎么寫的?
-
三個布爾運算符是什么?
-
寫出每個布爾運算符的真值表(即運算符的布爾值及其計算結(jié)果的每種可能組合)。
-
下面的表達式表示什么?
(5 > 4) and (3 == 5) not (5 > 4) (5 > 4) or (3 == 5) not ((5 > 4) or (3 == 5)) (True and True) and (True == False) (not False) or (not True)
-
六個比較運算符是什么?
-
等于運算符和賦值運算符有什么區(qū)別?
-
解釋什么是條件,以及在什么情況下使用條件。
-
識別代碼中的三個塊:
spam = 0 if spam == 10: print('eggs') if spam > 5: print('bacon') else: print('ham') print('spam') print('spam')
-
編寫代碼,如果
1
存儲在spam
中,則打印Hello
,如果2
存儲在spam
中,則打印Howdy
,如果其他內(nèi)容存儲在spam
中,則打印Greetings!
。 -
如果你的程序陷入無限循環(huán),你可以按什么鍵?
-
break
和continue
有什么區(qū)別? -
一個
for
循環(huán)中的range(10)
、range(0, 10)
、range(0, 10, 1)
有什么區(qū)別? -
寫一個簡短的程序,使用一個
for
循環(huán)打印數(shù)字1
到10
。然后編寫一個等價的程序,使用一個while
循環(huán)打印數(shù)字1
到10
。 -
如果你在一個名為
spam
的模塊中有一個名為bacon()
的函數(shù),你在導入spam
后會如何調(diào)用它?文章來源:http://www.zghlxwxcb.cn/news/detail-400935.html
附加題:在網(wǎng)上查一下round()
和abs()
函數(shù),看看它們是做什么的。在交互式 Shell 中試驗它們。文章來源地址http://www.zghlxwxcb.cn/news/detail-400935.html
到了這里,關(guān)于Python 自動化指南(繁瑣工作自動化)第二版:二、流程控制的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!