python3快速入門教程錯誤和異常
Python 中(至少)有兩種錯誤:語法錯誤(syntax errors)和異常(exceptions)。
語法錯誤
語法錯誤又稱作解析錯誤:
>>> while True print('Hello world') File "<stdin>", line 1 while True print('Hello world') ^ SyntaxError: invalid syntax
語法分析器指出錯誤行,並且在檢測到錯誤的位置前面顯示^。
異常
即使語句或表示式在語法上是正確的,執行時也可能會引發錯誤。執行期檢測到的錯誤稱為異常
>>> 10 * (1/0) Traceback (most recent call last): File "<stdin>", line 1, in <module> ZeroDivisionError: division by zero >>> 4 + spam*3 Traceback (most recent call last): File "<stdin>", line 1, in <module> NameError: name 'spam' is not defined >>> '2' + 2 Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: Can't convert 'int' object to str implicitly
錯誤資訊的最後一行指出發生了什麼錯誤。異常也有不同的型別,異常型別做為錯誤資訊的一部分顯示出來:示例中的異常分別為 ZeroDivisionError
, NameError
和 TypeError
。列印錯誤資訊時,異常的型別作為異常的內建名顯示。對於所有的內建異常都是如此,不過使用者自定義異常就不一定了(儘管這是很有用的約定)。標準異常名是內建的識別符號(非保留關鍵字)。
這行後一部分是關於該異常型別的詳細說明,這意味著它的內容依賴於異常型別。
錯誤資訊的前半部分以堆疊的形式列出異常發生的位置。通常在堆疊中列出了原始碼行,然而,來自標準輸入的原始碼不會顯示出來。
內建的異常 列出了內建異常和它們的含義。
異常處理
例子:要求使用者輸入直到輸入合法的整數為止,但允許使用者中斷這個程式(使用Control-C或系統支援的任何方法)。注意:使用者產生的中斷會引發 KeyboardInterrupt 異常。
>>> while True: ... try: ... x = int(input("Please enter a number: ")) ... break ... except ValueError: ... print("Oops! That was no valid number. Try again...") ...
try 語句按如下方式工作。
- 首先,執行try和except 之間的內容
- 如果沒有異常發生,忽略except語句。
- 如果在 try 子句執行過程中發生了異常,那麼該子句其餘的部分就會忽略。如果異常匹配於 * 後面指定的異常型別,就執行對應的except子句。然後繼續執行try語句之後的程式碼。
- 如果發生了一個異常,在except 子句中沒有與之匹配的分支,它就會傳遞到上一級try 語句中。如果最終仍找不到對應的處理語句,它就成為未處理異常,終止程式執行,顯示提示資訊。
try語句可能包含多個 except 子句,分別指定處理不同的異常。至多執行一個分支。異常處理程式只會處理對應的 try 子句中發生的異常,在同一try語句中,其他子句中發生的異常則不作處理。except 子句可以在元組中列出多個異常的名字,例如:
... except (RuntimeError, TypeError, NameError): ... pass
異常匹配如果except子句中的類相同的類或其基類(反之如果是其子類則不行)。 例如,以下程式碼將按此順序列印B,C,D:
class B(Exception): pass class C(B): pass class D(C): pass for cls in [B, C, D]: try: raise cls() except D: print("D") except C: print("C") except B: print("B")
如果except B在先,將列印B,B,B。
最後except子句可以省略例外名稱,作為萬用字元。 請謹慎使用此功能,因為這種方式很容易掩蓋真正的程式設計錯誤! 它也可以用來列印錯誤訊息,然後重新引發異常(也允許呼叫者處理異常):
import sys try: f = open('myfile.txt') s = f.readline() i = int(s.strip()) except OSError as err: print("OS error: {0}".format(err)) except ValueError: print("Could not convert data to an integer.") except: print("Unexpected error:", sys.exc_info()[0]) raise
try … except語句可以帶有else子句,該子句只能出現在所有 except 子句之後。當 try 語句沒有丟擲異常時,需要執行一些程式碼,可以使用這個子句。例如:
for arg in sys.argv[1:]: try: f = open(arg, 'r') except OSError: print('cannot open', arg) else: print(arg, 'has', len(f.readlines()), 'lines') f.close()
使用else子句比在try子句中附加程式碼要好,因為這樣可以避免 try … except 意外的截獲本來不屬於它們的那些程式碼丟擲的異常。
發生異常時,可能會有相關值,作為異常的引數存在。這個引數是否存在、是什麼型別,依賴於異常的型別。
在異常名(元組)之後,也可以為 except 子句指定一個變數。這個變數綁定於異常例項,它儲存在instance.args引數中。為了方便起見,異常例項定義了 str() ,這樣就可以直接訪問過列印引數而不必引用.args。也可以在引發異常之前初始化異常,並根據需要向其新增屬性。
>>> try: ... raise Exception('spam', 'eggs') ... except Exception as inst: ... print(type(inst)) # the exception instance ... print(inst.args) # arguments stored in .args ... print(inst) # __str__ allows args to be printed directly, ... # but may be overridden in exception subclasses ... x, y = inst.args # unpack args ... print('x =', x) ... print('y =', y) ... <class 'Exception'> ('spam', 'eggs') ('spam', 'eggs') x = spam y = eggs
對於那些未處理的異常,如果它們帶有引數,那麼就會被作為異常資訊的最後部分(“詳情”)打印出來。
異常處理器不僅僅處理那些在 try 子句中立刻發生的異常,也會處理那些 try 子句中呼叫的函式內部發生的異常。例如:
>>> try: ... raise Exception('spam', 'eggs') ... except Exception as inst: ... print(type(inst)) # the exception instance ... print(inst.args) # arguments stored in .args ... print(inst) # __str__ allows args to be printed directly, ... # but may be overridden in exception subclasses ... x, y = inst.args # unpack args ... print('x =', x) ... print('y =', y) ... <class 'Exception'> ('spam', 'eggs') ('spam', 'eggs') x = spam y = eggs
丟擲異常
raise 語句可強行丟擲指定的異常。例如:
>>> raise NameError('HiThere') Traceback (most recent call last): File "<stdin>", line 1, in <module> NameError: HiThere
要丟擲的異常由raise的唯一引數標識。它必需是異常例項或異常類(繼承自 Exception 的類)。如果傳遞異常類,它將通過呼叫它的沒有引數的建構函式而隱式例項化:
raise ValueError # shorthand for 'raise ValueError()'
如果你想知道異常是否丟擲,但不想處理它,raise語句可簡單的重新丟擲該異常:
>>> try: ... raise NameError('HiThere') ... except NameError: ... print('An exception flew by!') ... raise ... An exception flew by! Traceback (most recent call last): File "<stdin>", line 2, in <module> NameError: HiThere
使用者自定義異常
直接或間接的繼承 Exception 可以自定義異常。
異常類中可以定義任何其它類中可以定義的東西,但是通常為了保持簡單,只在其中加入幾個屬性資訊,以供異常處理控制代碼提取。如果新建立的模組中需要丟擲幾種不同的錯誤時,通常的作法是為該模組定義異常基類,然後針對不同的錯誤型別派生出對應的異常子類:
class Error(Exception): """Base class for exceptions in this module.""" pass class InputError(Error): """Exception raised for errors in the input. Attributes: expression -- input expression in which the error occurred message -- explanation of the error """ def __init__(self, expression, message): self.expression = expression self.message = message class TransitionError(Error): """Raised when an operation attempts a state transition that's not allowed. Attributes: previous -- state at beginning of transition next -- attempted new state message -- explanation of why the specific transition is not allowed """ def __init__(self, previous, next, message): self.previous = previous self.next = next self.message = message
與標準異常相似,大多數異常的命名都以 “Error” 結尾。
很多標準模組中都定義了自己的異常,以報告在他們所定義的函式中可能發生的錯誤。關於類的進一步資訊請參見Classes。
定義清理行為
try 語句有可選的子句定義在任何情況下都一定要執行的清理操作。例如:
>>> try: ... raise KeyboardInterrupt ... finally: ... print('Goodbye, world!') ... Goodbye, world! KeyboardInterrupt Traceback (most recent call last): File "<stdin>", line 2, in <module>
不管有沒有發生異常,finally子句在程式離開try前都會被執行。當語句中發生了未捕獲的異常(或者它發生在except或else子句中),在執行完finally子句後它會被重新丟擲。 try語句經由break、continue或return語句退出也會執行finally子句。
>>> def divide(x, y): ... try: ... result = x / y ... except ZeroDivisionError: ... print("division by zero!") ... else: ... print("result is", result) ... finally: ... print("executing finally clause") ... >>> divide(2, 1) result is 2.0 executing finally clause >>> divide(2, 0) division by zero! executing finally clause >>> divide("2", "1") executing finally clause Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 3, in divide TypeError: unsupported operand type(s) for /: 'str' and 'str'
finally子句多用於釋放外部資源(檔案或網路連線之類的)。
預定義清理行為
有些物件定義了標準的清理行為,無論物件操作是否成功,不再需要該物件的時候就會起作用。以下示例嘗試開啟檔案並把內容輸出到螢幕。
for line in open("myfile.txt"): print(line, end="")
這段程式碼的問題在於在程式碼執行完後沒有立即關閉開啟的檔案。簡單的腳本里沒什麼,但是大型應用程式就會出問題。with語句使得檔案之類的物件能及時準確地進行清理。
with open("myfile.txt") as f: for line in f: print(line, end="")
語句執行後,檔案f總會被關閉,即使是在處理檔案行時出錯也一樣。其它物件是否提供了預定義的清理行為要參考相關文件。
異常處理例項: 正則表示式及拼音排序
有某群的某段聊天記錄
現在要求輸出排序的qq名,結果類似如下:
[..., '本草隱士', 'jerryyu', '可憐的櫻桃樹', '叻風雲', '歐陽-深圳白芒', ...]
需求來源:有個想批量邀請某些qq群的活躍使用者到自己的群。又不想鋪天蓋地去看聊天記錄。
參考資料:python文字處理
參考程式碼:
#!/usr/bin/python3 # -*- coding: utf-8 -*- # Author: [email protected] wechat:pythontesting qq:37391319 # 技術支援 釘釘群:21745728(可以加釘釘pythontesting邀請加入) # qq群:144081101 591302926 567351477 # CreateDate: 2018-6-1 import re from pypinyin import lazy_pinyin name = r'test.txt' text = open(name,encoding='utf-8').read() #print(text) results = re.findall(r'(:\d+)\s(.*?)\(\d+', text) names = set() for item in results: names.add(item[1]) keys = list(names) keys = sorted(keys) def compare(char): try: result = lazy_pinyin(char)[0][0] except Exception as e: result = char return result keys.sort(key=compare) print(keys)
執行示例:
1,把qq群的聊天記錄匯出為txt格式,重新命名為test.txt
2, 執行:
$ python3 qq.py ['Sally', '^^O^^', 'aa催乳師', 'bling', '本草隱士', '純中藥治療陽痿早洩', '長夜無荒', '東方~慈航', '幹金草', '廣東-曾超慶', '紅梅* 渝', 'jerryyu', '可憐的櫻桃樹', '叻風雲', '歐陽-深圳白芒', '勝昔堂~元亨', '蜀中~眉豆。', '陝西渭南逸清閣*無為', '吳寧……任', '系統訊息', '於立偉', '倚窗望嶽', '煙霞靄靄', '燕子', '張強', '滋味', '✾買個罐頭 吃西餐', '【大俠】好好', '【大俠】面向大海~純中藥治燙傷', '【宗師】吳寧……任', '【宗師】紅梅* 渝', '【少俠】焚琴煮鶴', '【少俠】笨笨', '【掌門】漵浦☞山野人家']
參考資料
- python測試等IT技術支援qq群: 887934385(後期會錄製視訊)