[ 導 讀 ]條件語句透過一個或多個布林表示式的執行結果(真值或假值)決定下一步的執行方向。所謂布林表示式,即對某個物件進行布林運算,產生一個bool值。條件語句的執行邏輯為:如果條件被滿足(返回真值),可以做某件事情;如果條件不滿足(返回假值),就做另一件事情,或什麼也不做。
透過圖1,可以對條件語句的執行機制作有一個簡單的瞭解。虛線框內是一個選擇結構,此結構中包含一個判斷條件和兩條執行語句,以及連線各部分的流向線。根據判斷條件(布林表示式)返回值的情況,程式將選擇執行語句1或語句2。
在Python中,實現選擇結構最普遍的工具是if語句。此外,try語句專門用於異常處理,其內在邏輯也符合選擇結構。
01 if、elif與else
if語句中包含3種條件判斷句式,即if、elif和else。其中,if與elif部分都包含判斷條件,當判斷條件都不成立時,程式才能執行else部分的程式碼。
if語句最基礎的形式是if-else,其基本語法格式如下。
if 條件表示式: 操作語句1 else: 操作語句2
if-else語句常用的引數及說明:
條件表示式:接收布林表示式,表示判斷條件是否成立。無預設值
操作語句:接收操作語句,表示執行一段程式碼。無預設值
if-else語句執行時,程式首先判斷if部分條件表示式的真假。如果條件表示式返回真值,則執行操作語句1;如果返回假值,則執行操作語句2。
if-else語句的形式很簡單,透過條件判斷的結果即可決定下一步的執行方向,具有兩條分支。以編寫一個賬戶密碼登入介面為例,介紹該語句的使用,如程式碼清單①所示。
程式碼清單① if-else語句實現登入介面
In[1]: name = input ('請輸入使用者名稱:') password = input ('請輸入密碼:') if name == "Lucy" and password == "123456": print ('****登入成功,歡迎!*****') else: print ('-----您的輸入有誤,登入失敗!-----') Out[1]: 請輸入使用者名稱:Lucy 請輸入密碼:123 -----您的輸入有誤,登入失敗!----- In[2]: name = input ('請輸入使用者名稱:') password = input ('請輸入密碼:') if name == "Lucy" and password == "123456": print ('****登入成功,歡迎!*****') else: print ('-----您的輸入有誤,登入失敗!-----') Out[2]: 請輸入使用者名稱:Lucy 請輸入密碼:123456 ****登入成功,歡迎!*****
在程式碼清單①中,使用input函式以支援互動式的輸入,並在函式括號內插入文字進行了輸入提示,增強了登入介面的人性化。在if部分的條件判斷式中,使用and運算子進行且運算,只有賬戶和密碼都輸入正確才能成功登入,從而增加了安全性。
if-else語句可以縮減為單行形式,其基本語法格式如下。
操作語句1 if 條件表示式 else 操作語句2
if-else語句單行形式語法格式中的引數說明與圖1一致。如果條件表示式返回的結果為真,則執行if前面的操作語句1,否則執行else後面的操作語句2。
if-else語句使用單行形式的目的主要在於增加程式碼的簡潔性,其基本使用方法如程式碼清單②所示。
程式碼清單② if-else語句的單行形式
In[3]: num1, num2 = 11, 90 print('num1加num2為百分數') if 1000 > num1 + num2 >100 else print('num1加num2不為百分數') Out[3]: num1加num2為百分數
if-else語句有明顯的缺陷,即只能實現兩條分支。實際工作中需要用到的條件分支數目可能難以想象,擴充套件if語句的分支需要用到elif句式。elif是“else if”的縮寫,即“下一條件是否成立?”
使用elif有簡潔、減少過分縮排的效果。將elif程式碼塊放在if和else之間,就組成了if-elif-else語句。理論上,if語句中的elif可以無限多。if-elif-else語句與if-else語句其實是等價的,後者相當於前者中elif個數為0或不執行的情況。由於if-elif-else語句能提供更多條件分支,因此被普遍使用,其基本語法格式如下。
if 條件表示式1: 操作語句1 elif 條件表示式2: 操作語句2 else: 操作語句3
if-elif-else語句語法格式中的引數與上文說明一致。該語句執行時,按照從上到下的順序,依次檢查每個條件表示式返回值的情況,任何一個條件表示式返回真值,就執行該表示式下面的操作語句,若所有條件表示式都返回假值,則執行else下面的操作語句。
if-elif-else語句相對於if-else語句優勢明顯,可以實現更為複雜的功能。使用if-elif-else語句實現年齡段的判斷,如程式碼清單③所示。
程式碼清單③ 使用if-elif-else語句實現年齡段的判斷
In[4]: age = input('請輸入您的年齡:') age = int(age) if age < 18: print('未成年人!') elif age >= 18 and age <= 25: print('青年人!') elif age > 25 and age <= 60: print('中年人!') else: print('老年人!') Out[4]: 請輸入您的年齡: 20 青年人!
程式碼清單③透過比較運算子實現了年齡段劃分,並能區分年齡段界限,避免邏輯出錯。input函式將接收的任何資料型別都預設為str,如果不在該程式碼中插入轉換接收資料型別的語句,程式將無法執行。這是因為,接收的年齡資料會被用於和後續的年齡數值比較,而number與str是無法比較的。
需要說明,if語句還有一種形式是if-if-else,這一形式中的if可以有多個,從而實現多分支。與if-elif-else語句相比,差異不僅僅存在於形式上,效能上也同樣有區別,使用多個if的效率更低,它實際上是多重if語句。
if語句支援巢狀,即在一個if語句中嵌入另一個if語句,從而構成不同層次的選擇結構。巢狀的意義在於實現多層選擇結構。使用巢狀對條件語句的功能有昇華作用,這與elif是相似的,elif將有限的條件分支擴充套件,巢狀則提供了建立多層選擇結構的工具,兩者分別在不同的維度上提升了if語句的功能性。使用巢狀需要以不同的縮排長度劃分程式碼結構的層次,因此巢狀時要特別注意縮排的規範性。
巢狀選擇結構具有很廣的應用場景,以下給出一個例子。假設系統中儲存了5個使用者的身份資訊,分別是:來自英國的Tom,35歲;來自法國的Frank,35歲;來自德國的Bob,35歲;來自澳大利亞的Washington,51歲;來自南非的Jane,21歲。
設計一個程式,詢問使用者的部分資訊,在對方不說出自己名字的情況下識別其身份,如程式碼清單④所示。
程式碼清單④ 巢狀if-elif-else語句
In[5]: age = input('請輸入你的年齡:') age = int(age) if age == 35: nation = input('請輸入你的國籍:') if nation == '英國': print('你是Tom! ') elif (nation == '法國'): print('你是Frank! ') else: print('你是Bob! ') elif age == 21: print('你是Jane,來自南非! ') elif age == 51: print('你是Washington,來自澳大利亞! ') else: print('請輸入正確年齡值! ') Out[5]: 請輸入你的年齡:35 請輸入你的國籍:法國 你是Frank!
從程式碼清單④可以看到,該程式具有兩層選擇結構。第1層用於詢問年齡,程式透過接收的年齡,可以判斷輸入者是Jane、Washington或其他3個同齡人中的一個;若收到的值不在這5人年齡範圍中,則提示輸入出錯;若收到的值是3個同齡人的歲數,則進入下一層選擇結構,即詢問國籍;透過詢問國籍,程式可以準確報出輸入者的資訊。
使用if語句時,需要注意以下幾點。
條件判斷語句應儘量簡單,若語句複雜則應當將運算先放到一個變數中。
Python的條件語句中允許常用的數值比較運算(==,!=,>,>=,<,<=)。
Python允許無限次if語句巢狀,但實際程式設計中如果必須用到3級到4級巢狀,建議考慮用其他方法編寫程式碼,巢狀超過兩層會使程式的執行效率大打折扣。
2. try、except與else
如果執行途中發生錯誤事件,程式的執行將中斷,並建立異常物件。異常是程式在正常流程控制以外採取的動作,當它被引發時,計算機將自動尋找異常處理程式,以幫助程式恢復正常執行。
要保證程式的正常執行,就需要排除錯誤,錯誤要麼是語法上的,要麼是邏輯上的。語法錯誤的出現表明程式在結構上出現了問題,可以在程式執行前加以糾正。邏輯錯誤可能是缺少輸入或輸入不正確,某些情況下,也可能是根據輸入無法生成預期的結果。邏輯錯誤難以預防,必須使用異常處理程式來應對。
計算機語言針對可能出現的錯誤定義了異常型別,某種錯誤引發對應的異常時,異常處理程式將被啟動,從而恢復程式的正常執行。Python中定義的異常型別大致分為數值計算錯誤、作業系統錯誤、無效資料查詢、Unicode相關的錯誤和警告等幾類,如下所示。
Python異常類:
BaseException:所有異常的基類
Exception:常規異常的基類
StandardError:所有的內建標準異常的基類
ArithmeticError:所有數值計算異常的基類
FloatingPointError:浮點計算異常
OverflowError:數值運算超出最大限制
ZeroDivisionError:除零
AssertionError:斷言語句失敗
AttributeError:物件不包含某個屬性
EOFError:沒有內建輸入,到達EOF標記
EnvironmentError:作業系統異常的基類
IOError:輸入/輸出操作失敗
OSError:作業系統異常
WindowsError:系統呼叫失敗
ImportError:匯入模組/物件失敗
KeyboardInterrupt:使用者中斷執行
LookupError:無效資料查詢的基類
IndexError:序列中沒有此索引
KeyError:對映中沒有這個鍵
MemoryError:記憶體溢位異常
NameError:未宣告/初始化物件
UnboundLocalError:訪問未初始化的本地變數
ReferenceError:弱引用試圖訪問已經垃圾回收了的物件
RuntimeError:一般的執行時異常
NotImplementedError:尚未實現的方法
SyntaxError:語法錯誤導致的異常
IndentationError:縮排錯誤導致的異常
TabError:Tab和空格混用
SystemError:一般的直譯器系統異常
TypeError:對型別無效的操作
ValueError:傳入無效的引數
UnicodeError:Unicode相關的異常
UnicodeDecodeError:Unicode解碼時的異常
UnicodeEncodeError:Unicode編碼錯誤導致的異常
UnicodeTranslateError:Unicode轉換錯誤導致的異常
Warning:警告的基類
DeprecationWarning:關於被棄用的特徵的警告
FutureWarning:關於構造將來語義會有改變的警告
UserWarning:使用者程式碼生成的警告
PendingDeprecationWarning:關於特性將會被廢棄的警告
RuntimeWarning:可疑的執行時行為(runtime behavior)的警告
SyntaxWarning:可疑語法的警告
ImportWarning:用於在匯入模組過程中觸發的警告
UnicodeWarning:與Unicode相關的警告
BytesWarning:與位元組或位元組碼相關的警告
ResourceWarning:與資源使用相關的警告
異常體系內部有層次關係,即某些異常屬於某個異常的子類,該異常又可能是另一異常的子類。較低層次、更具細節的異常是某些異常的子類,這些高層次的異常則稱為基類,子類和基類是相對的。Python異常體系中的部分關係如圖2所示。
在圖2中,越下面的異常,其層次越低,細節更明顯,它們總有更高層次的基類。
Python使用try語句處理異常,該語句一般包括try、except和else三個句式,組成try-except-else的形式。try部分包含一個嘗試執行的程式碼塊,except部分是特定異常的處理對策,else部分則在程式執行正常時執行。
try語句可以視為一種條件分支,與if語句的區別是try語句並不包含條件判斷式,執行的流向也不取決於條件表示式,而依賴於程式碼塊能否執行。但其內在邏輯和執行流程與if語句是相似的,符合條件分支的特徵,其基本語法格式如下。
try: 操作語句1 except 錯誤型別1: 操作語句2 except 錯誤型別2: 操作語句3 else: 操作語句4
try語句常用的語法格式及其引數說明如下所示。
try-except-else語句常用的語法格式及其引數說明:
錯誤型別:接收Python異常名,表示符合該異常則執行下面語句。無預設值
操作語句:接收操作語句,表示執行一段程式碼。無預設值
執行try-except-else語句時,程式首先執行try程式碼塊,即可能出錯的試探性語句,這可能導致致命性錯誤使得程式無法繼續執行。如果try程式碼塊確實無法執行,就可能執行某個except程式碼塊。
執行一個except程式碼塊的條件是,系統捕捉的異常型別和該程式碼塊標識的型別相符合;如果try程式碼塊的語句正常執行,就接著執行else程式碼塊的語句。
如果try部分無法執行,也沒有找到相應的except程式碼塊,就將異常訊息傳送給程式呼叫端,如Python Shell,Python Shell對異常訊息的預設處理則是終止程式的執行並列印具體的出錯資訊,這也是在Python Shell中執行程式錯誤後所出現的出錯列印資訊的由來。
在try語句中,except與else程式碼塊都是可選的。except程式碼塊可以有0或多個;else程式碼塊可以有0或1個。但要注意,else語句的存在必須以except語句的存在為前提,在沒有except語句的try語句中使用else語句,會引發語法錯誤。
try語句中沒有else時,就構成try-except語句,如程式碼清單⑤所示。
程式碼清單⑤ try語句處理除零異常
In[6]: number = 0 # 以變數number作被除數,嘗試執行除法操作 try: print('1.0 / number =', 1.0 / number) # 如果異常是除零異常,輸出提示資訊 except ZeroDivisionError: print('***除數為0***') Out[6]: ***除數為0***
在程式碼清單⑤中,由於0不能做除數,因此引發了除零異常。except程式碼塊由於給出了ZeroDivisionError的解決方案,因此被執行,程式得以完整地執行。
程式碼清單⑤所展示的異常之間的層次差別是有意義的,這在程式執行過程中可以體現,如程式碼清單⑥所示。
程式碼清單⑥ Python異常層次差異
In[7]: dict1={'a': 1, 'b': 2, 'v': 22} # 嘗試索引賦值dict中不存在的值 try: x = dict1['y'] except LookupError: print('查詢錯誤') except KeyError: print('鍵錯誤') else: print(x) Out[7]: 查詢錯誤 In[8]: # 調換LookupError和KeyError處理程式碼塊的順序 dict2={'a': 1, 'b': 2, 'v': 22} # 嘗試索引賦值dict中不存在的值 try: x = dict2['y'] except KeyError: print('鍵錯誤') except LookupError: print('查詢錯誤') else: print(x) Out[8]: 鍵錯誤
程式碼清單⑥展示的try-except-else語句嘗試查詢不在dict中的鍵值對,從而引發了異常。這一異常準確地說應屬於KeyError,但由於KeyError是LookupError的子類,且在程式碼清單⑥中將LookupError置於KeyError之前,因此程式優先執行該except程式碼塊。所以,使用多個except程式碼塊時,必須堅持對其規範排序,要從最具針對性的異常到最通用的異常。
除自然發生的異常外,Python中的raise語句可用於故意引發異常。使用該語句引發異常時,只需在raise後輸入異常名即可,如程式碼清單⑦所示。
程式碼清單⑦ raise語句
In[9]: # 嘗試引發IndexError try: raise IndexError except KeyError: print ('in KeyError except') except IndexError: print('in IndexError except') else: print('no exception') Out[9]: in IndexError except