目錄 | 上一節 (3.2 深刻函數) | [下一節 (3.4 模塊)]()html
雖然前面已經介紹了異常,但本節補充一些有關錯誤檢查和異常處理的其它細節。python
Python 不對函數參數類型或值進行檢查或者校驗。函數能夠處理與函數內部語句兼容的任何數據。git
def add(x, y): return x + y add(3, 4) # 7 add('Hello', 'World') # 'HelloWorld' add('3', '4') # '34'
若是函數中有錯誤,它們將(做爲異常)在運行時出現。github
def add(x, y): return x + y >>> add(3, '4') Traceback (most recent call last): ... TypeError: unsupported operand type(s) for +: 'int' and 'str' >>>
爲了驗證代碼,強烈建議進行測試(稍後介紹)。編程
異經常使用於發出錯誤信號。segmentfault
要本身觸發異常,請使用 raise
語句:安全
if name not in authorized: raise RuntimeError(f'{name} not authorized')
要捕獲異常,請使用 try-except
語句:函數
try: authenticate(username) except RuntimeError as e: print(e)
異常傳遞到第一個匹配的 except
:測試
def grok(): ... raise RuntimeError('Whoa!') # Exception raised here def spam(): grok() # Call that will raise exception def bar(): try: spam() except RuntimeError as e: # Exception caught here ... def foo(): try: bar() except RuntimeError as e: # Exception does NOT arrive here ... foo()
要處理異常,請將語句放到 except
塊裏面。 except
塊裏面能夠添加要處理該錯誤的任何語句。ui
def grok(): ... raise RuntimeError('Whoa!') def bar(): try: grok() except RuntimeError as e: # Exception caught here statements # Use this statements statements ... bar()
異常處理以後,從 try-except
以後的第一個語句繼續執行。
def grok(): ... raise RuntimeError('Whoa!') def bar(): try: grok() except RuntimeError as e: # Exception caught here statements statements ... statements # Resumes execution here statements # And continues here ... bar()
有很是多的內建異常。一般,異常名稱代表出了什麼問題(例如,由於提供錯誤的值而觸發 ValueError
)。下述列表不是一份詳盡的清單,請訪問 文檔 以獲取更多信息。
ArithmeticError AssertionError EnvironmentError EOFError ImportError IndexError KeyboardInterrupt KeyError MemoryError NameError ReferenceError RuntimeError SyntaxError SystemError TypeError ValueError
異常具備一個關聯值。它包含有關錯誤的更明確的信息。
raise RuntimeError('Invalid user name')
這個值是異常實例的一部分,它被放置在提供給 except
的變量中。
try: ... except RuntimeError as e: # `e` holds the exception raised ...
e
是異常類型的一個實例。可是,當打印的時候,它一般看起來像一個字符串。
except RuntimeError as e: print('Failed : Reason', e)
可使用多個 except
塊捕獲不一樣類型的異常:
try: ... except LookupError as e: ... except RuntimeError as e: ... except IOError as e: ... except KeyboardInterrupt as e: ...
或者,若是處理不一樣異常的語句是相同的,則能夠對它們進行分組:
try: ... except (IOError,LookupError,RuntimeError) as e: ...
要捕獲全部的異常,請使用 Exception
。以下所示:
try: ... except Exception: # DANGER. See below print('An error occurred')
一般,像這樣編寫代碼是個壞主意,由於這說明不知道程序爲何會失敗。
這裏是一個使用異常的錯誤方式。
try: go_do_something() except Exception: print('Computer says no')
這將捕獲全部可能的錯誤,而且,當代碼由於某些根本沒想到的緣由(如卸載 Python 模塊等)運行失敗時,可能沒法進行調試。
若是想要捕獲全部的錯誤,這有一個更明智的方法。
try: go_do_something() except Exception as e: print('Computer says no. Reason :', e)
它報告了失敗的明確緣由。當編寫捕獲全部可能異常的代碼時,擁有查看/報告錯誤的機制幾乎老是一個好主意。
不過,一般來講,最好在合理的範圍內儘可能窄地捕獲異常。僅捕獲能處理的異常。讓其它錯誤經過——也許其它代碼能夠處理。
使用 raise
傳遞捕獲的錯誤。
try: go_do_something() except Exception as e: print('Computer says no. Reason :', e) raise
這容許你採起措施(例如:記錄日誌)並將錯誤傳遞給調用者。
不要捕獲異常,而是失敗發生時「中止運行,發出預警」(Fail fast and loud)。若是重要的話,別人會處理的。只有你是那我的的時候才捕獲異常。即,只捕獲能夠恢復並正常運行的錯誤。
finally
語句finally
語句指定不管是否發生異常都必須運行的代碼。
lock = Lock() ... lock.acquire() try: ... finally: lock.release() # this will ALWAYS be executed. With and without exception.
一般使用 finally
語句安全地管理資源(尤爲是鎖,文件等)。
with
語句在現代代碼中,try-finally
語句一般被 with
語句取代。
lock = Lock() with lock: # lock acquired ... # lock released
一個更熟悉的例子:
with open(filename) as f: # Use the file ... # File closed
with
語句定義資源的使用上下文。當執行離開上下文時,資源被釋放。with
語句僅適用於通過專門編程以支持它的某些對象。
在上一節中編寫的 parse_csv()
函數容許選擇用戶指定的列,可是隻有輸入數據文件具備列標題時纔會生效。
請修改代碼,以便在同時傳遞 select
和 has_headers=False
參數時觸發異常。例如:
>>> parse_csv('Data/prices.csv', select=['name','price'], has_headers=False) Traceback (most recent call last): File "<stdin>", line 1, in <module> File "fileparse.py", line 9, in parse_csv raise RuntimeError("select argument requires column headers") RuntimeError: select argument requires column headers >>>
添加此檢查後,你可能會問是否應該在函數中執行其它類型的完整性檢查。例如,檢查文件名是字符串,列表仍是其它類型?
通常來講,最好是跳過此類測試,輸入錯誤的時候讓程序運行失敗。回溯信息會指出問題的根源,而且幫助調試。
添加上述檢查的主要緣由是爲了不在無心義的模式下運行代碼(例如,使用要求列標題的特性,可是同時指定沒有標題)。
這代表調用代碼部分出現一個編程錯誤。檢查「不該發生」的狀況一般是個好主意。
你編寫的 parse_csv()
函數用於處理文件的所有內容。可是,在現實世界中,輸入文件可能包含損壞的數據,丟失的數據或者髒數據。嘗試下面這個實驗:
>>> portfolio = parse_csv('Data/missing.csv', types=[str, int, float]) Traceback (most recent call last): File "<stdin>", line 1, in <module> File "fileparse.py", line 36, in parse_csv row = [func(val) for func, val in zip(types, row)] ValueError: invalid literal for int() with base 10: '' >>>
請修改 parse_csv()
函數以便捕獲全部在記錄建立期間生成的 ValueError
異常,併爲沒法轉換的行打印警告消息。
錯誤消息應該包括行號以及有關失敗緣由的信息。要測試函數,嘗試讀取上面的 Data/missing.csv
文件,例如:
>>> portfolio = parse_csv('Data/missing.csv', types=[str, int, float]) Row 4: Couldn't convert ['MSFT', '', '51.23'] Row 4: Reason invalid literal for int() with base 10: '' Row 7: Couldn't convert ['IBM', '', '70.44'] Row 7: Reason invalid literal for int() with base 10: '' >>> >>> portfolio [{'price': 32.2, 'name': 'AA', 'shares': 100}, {'price': 91.1, 'name': 'IBM', 'shares': 50}, {'price': 83.44, 'name': 'CAT', 'shares': 150}, {'price': 40.37, 'name': 'GE', 'shares': 95}, {'price': 65.1, 'name': 'MSFT', 'shares': 50}] >>>
請修改 parse_csv()
函數,以便用戶明確須要時能夠隱藏解析的錯誤消息,例如:
>>> portfolio = parse_csv('Data/missing.csv', types=[str,int,float], silence_errors=True) >>> portfolio [{'price': 32.2, 'name': 'AA', 'shares': 100}, {'price': 91.1, 'name': 'IBM', 'shares': 50}, {'price': 83.44, 'name': 'CAT', 'shares': 150}, {'price': 40.37, 'name': 'GE', 'shares': 95}, {'price': 65.1, 'name': 'MSFT', 'shares': 50}] >>>
在大部分的程序中,錯誤處理是最難作好的事情之一。通常來講,不該該默默地忽略錯誤。相反,最好是報告問題,而且讓用戶選擇是否隱藏錯誤信息(若是它們選擇這樣作)。
目錄 | 上一節 (3.2 深刻函數) | [下一節 (3.4 模塊)]()