資料來(lái)源: https://book./details/321/
介紹
在編程過(guò)程中為了增加友好性,在程序出現(xiàn)bug時(shí)一般不會(huì)將錯(cuò)誤信息顯示給用戶讓用戶蒙逼,而是顯示一個(gè)更友好的提示信息。
語(yǔ)法
try:
"""your code"""
except Exception:
"""上面的程序執(zhí)行出錯(cuò)后就指行這里的代碼"""
需求:將用戶輸入的兩個(gè)數(shù)字相加
while True:
num1 = input('num1:')
num2 = input('num2:')
try:
num1 = int(num1)
num2 = int(num2)
result = num1 + num2
except Exception as e:
print('出現(xiàn)異常,信息如下:')
print(e)
輸入的不是數(shù)字的話,執(zhí)行出錯(cuò)的異常就會(huì)把捕捉到 
像上面這個(gè)Exception異常,幾乎能捕捉到所有的錯(cuò)誤,這不一定是好事,因?yàn)槌绦虺鲥e(cuò)的原因有很多種,我可能希望出不同的錯(cuò)誤就執(zhí)行不同的異常處理邏輯。 全執(zhí)行同一邏輯的話會(huì)增加程序調(diào)試難度,因?yàn)槟悴恢朗鞘裁丛驅(qū)е碌腻e(cuò)誤
異常類型
內(nèi)置異常類的繼承關(guān)系
BaseException ------------------------------------- 所有異常的基類
+-- SystemExit ----------------------------------- 解釋器請(qǐng)求退出
+-- KeyboardInterrupt --------------------------- 用戶中斷執(zhí)行(通常是輸入^C)
+-- GeneratorExit -------------------------------- 生成器(generator)發(fā)生異常來(lái)通知退出
+-- Exception ----------------------------------- 常規(guī)錯(cuò)誤的基類
+-- StopIteration --------------------------- 迭代器沒(méi)有更多的值
+-- StopAsyncIteration
+-- ArithmeticError ------------------------ 所有數(shù)值計(jì)算錯(cuò)誤的基類
| +-- FloatingPointError ----------------- 浮點(diǎn)計(jì)算錯(cuò)誤
| +-- OverflowError ---------------------- 數(shù)值運(yùn)算超出最大限制
| +-- ZeroDivisionError ------------------ 除(或取模)零 (所有數(shù)據(jù)類型)
+-- AssertionError -------------------------- 斷言語(yǔ)句失敗
+-- AttributeError -------------------------- 對(duì)象沒(méi)有這個(gè)屬性
+-- BufferError
+-- EOFError -------------------------------- 沒(méi)有內(nèi)建輸入,到達(dá)EOF 標(biāo)記
+-- ImportError ----------------------------- 導(dǎo)入模塊/對(duì)象失敗
| +-- ModuleNotFoundError
+-- LookupError ----------------------------- 無(wú)效數(shù)據(jù)查詢的基類
| +-- IndexError ------------------------- 序列中沒(méi)有此索引(index)
| +-- KeyError --------------------------- 映射中沒(méi)有這個(gè)鍵
+-- MemoryError ----------------------------- 內(nèi)存溢出錯(cuò)誤(對(duì)于Python 解釋器不是致命的)
+-- NameError ------------------------------- 未聲明/初始化對(duì)象 (沒(méi)有屬性)
| +-- UnboundLocalError ------------------ 訪問(wèn)未初始化的本地變量
+-- OSError --------------------------------- 操作系統(tǒng)錯(cuò)誤
| +-- BlockingIOError
| +-- ChildProcessError
| +-- ConnectionError
| | +-- BrokenPipeError
| | +-- ConnectionAbortedError
| | +-- ConnectionRefusedError
| | +-- ConnectionResetError
| +-- FileExistsError
| +-- FileNotFoundError
| +-- InterruptedError
| +-- IsADirectoryError
| +-- NotADirectoryError
| +-- PermissionError
| +-- ProcessLookupError
| +-- TimeoutError
+-- ReferenceError -------------------------- 弱引用(Weak reference)試圖訪問(wèn)已經(jīng)垃圾回收了的對(duì)象
+-- RuntimeError ---------------------------- 一般的運(yùn)行時(shí)錯(cuò)誤
| +-- NotImplementedError ---------------- 尚未實(shí)現(xiàn)的方法
| +-- RecursionError
+-- SyntaxError ----------------------------- Python 語(yǔ)法錯(cuò)誤
| +-- IndentationError ------------------- 縮進(jìn)錯(cuò)誤
| +-- TabError ---------------------- Tab 和空格混用
+-- SystemError ----------------------------- 一般的解釋器系統(tǒng)錯(cuò)誤
+-- TypeError ------------------------------- 對(duì)類型無(wú)效的操作
+-- ValueError ------------------------------ 傳入無(wú)效的參數(shù)
| +-- UnicodeError ----------------------- Unicode 相關(guān)的錯(cuò)誤
| +-- UnicodeDecodeError ------------ Unicode 解碼時(shí)的錯(cuò)誤
| +-- UnicodeEncodeError ------------ Unicode 編碼時(shí)錯(cuò)誤
| +-- UnicodeTranslateError --------- Unicode 轉(zhuǎn)換時(shí)錯(cuò)誤
+-- Warning --------------------------------- 警告的基類
+-- DeprecationWarning ----------------- 關(guān)于被棄用的特征的警告
+-- PendingDeprecationWarning ---------- 關(guān)于特性將會(huì)被廢棄的警告
+-- RuntimeWarning --------------------- 可疑的運(yùn)行時(shí)行為(runtime behavior)的警告
+-- SyntaxWarning ---------------------- 可疑的語(yǔ)法的警告
+-- UserWarning ------------------------ 用戶代碼生成的警告
+-- FutureWarning ---------------------- 關(guān)于構(gòu)造將來(lái)語(yǔ)義會(huì)有改變的警告
+-- ImportWarning
+-- UnicodeWarning
+-- BytesWarning
+-- ResourceWarning
常見(jiàn)異常類型
- AttributeError 試圖訪問(wèn)一個(gè)對(duì)象沒(méi)有的屬性,比如foo.x,但是foo沒(méi)有屬性x
class A:
pass
try:
a = A()
print(a.name) # name不存在
except AttributeError as e:
print(e) # 'A' object has no attribute 'name'
- IOError 輸入/輸出異常;基本上是無(wú)法打開(kāi)文件
try:
f = open("不存在的文件.txt", mode="r", encoding="utf-8")
except IOError as e:
print(e) # [Errno 2] No such file or directory: '不存在的文件.txt'
- ImportError 無(wú)法引入模塊或包;基本上是路徑問(wèn)題或名稱錯(cuò)誤
import importlib
try:
metaclass = importlib.import_module("import_lib.metaclasss")
except ImportError as e:
print(e) # No module named 'import_lib.metaclasss'
- IndexError 下標(biāo)索引超出序列邊界,比如當(dāng)x只有三個(gè)元素,卻試圖訪問(wèn)x[5]
try:
a = [1, 2, 3]
print(a[9])
except IndexError as e:
print(e)
- KeyError 試圖訪問(wèn)字典里不存在的鍵
try:
a = {"name": "張小小"}
print(a["age"])
except KeyError as e:
print(e) # age
- KeyboardInterrupt Ctrl+C被按下
"""
命令行程序運(yùn)行期間,如果用戶想終止程序,一般都會(huì)采用Ctrl-C快捷鍵,
這個(gè)快捷鍵會(huì)引發(fā)python程序拋出KeyboardInterrupt異常。
我們可以捕獲這個(gè)異常,在用戶按下Ctrl-C的時(shí)候,進(jìn)行一些清理工作。
注意,攜程except Exception將無(wú)法捕獲KeyboardInterrupt異常。
"""
try:
# many code here
except KeyboardInterrupt as e:
# do something
- NameError 使用一個(gè)還未被賦予對(duì)象的變量
try:
print(aa) # aa未定義
except NameError as e:
print(e) # name 'aa' is not defined
- TypeError 傳入對(duì)象類型與要求的不符合
try:
print(float(['3']))
except TypeError as e:
print(e) # float() argument must be a string or a number, not 'list'
- ValueError 傳入一個(gè)調(diào)用者不期望的值,即使值的類型是正確的
"""
float函數(shù)可以接受字符串,即float('5'),只是float('string')中的值'string'是不合適的(不可轉(zhuǎn)換的)字符串
另一方面
因此,如果您嘗試float(['5']),就會(huì)得到一個(gè)TypeError,因?yàn)榱斜碛肋h(yuǎn)不能轉(zhuǎn)換為浮點(diǎn)。
"""
try:
print(float('ooo'))
except ValueError as e:
print(e) # could not convert string to float: 'ooo'
import math
try:
print(math.sqrt(-10))
except ValueError as e:
print(e) # math domain error
- UnboundLocalError 試圖訪問(wèn)一個(gè)還未被設(shè)置的局部變量,基本上是由于另有一個(gè)同名的全局變量,導(dǎo)致你以為正在訪問(wèn)它
- IndentationError 語(yǔ)法錯(cuò)誤(的子類);代碼沒(méi)有正確對(duì)齊
- SyntaxError Python代碼非法,代碼不能編譯(個(gè)人認(rèn)為這是語(yǔ)法錯(cuò)誤,寫錯(cuò)了)
捕獲多個(gè)異常
寫程序時(shí)需要考慮到try代碼塊中可能出現(xiàn)的任意異常,可以這樣寫:
s1 = 'hello'
try:
int(s1)
except IndexError as e:
print(e)
except KeyError as e:
print(e)
except ValueError as e:
print(e)
如果上面3個(gè)異常依然沒(méi)有匹配到對(duì)應(yīng)的錯(cuò)誤 怎么辦? 可以在程序最后加上Exception這個(gè)萬(wàn)能異常。
一個(gè)except還可以同時(shí)包含多個(gè)異常,即相當(dāng)于我們對(duì)某幾個(gè)不同的異常,采用同樣的處理 except后面跟著的多個(gè)異常類型,要把它們寫成tuple的形式。
import os
def test_exception2(num):
try:
a = 1/num
if num == 1: os.remove('NotExistedFile')
except (ZeroDivisionError, FileNotFoundError) as e:
print('3---', repr(e))
test_exception2(0)
ZeroDivisionError('division by zero')
test_exception2(1)
FileNotFoundError(2, 'No such file or directory')
其它異常結(jié)構(gòu)
try:
# 主代碼塊
pass
except KeyError,e:
# 異常時(shí),執(zhí)行該塊
pass
else:
# 主代碼塊執(zhí)行完,若未觸發(fā)任何異常,執(zhí)行該塊
pass
finally:
# 無(wú)論監(jiān)測(cè)的代碼是否發(fā)生異常,都執(zhí)行該處代碼
pass
主動(dòng)觸發(fā)異常raise
try:
raise Exception('錯(cuò)誤了。。。')
except Exception as e:
print(e)
raise是更好的return
該資料來(lái)源:https://www./archives/1175
編寫軟件,大部分代碼都是在處理各種異常和錯(cuò)誤。我們常常會(huì)遇到這樣的場(chǎng)景,代碼流程需要一層層的判斷底層的返回是否成功, 這樣的代碼寫起來(lái)其實(shí)很費(fèi)勁,為了一個(gè)可能出現(xiàn)的錯(cuò)誤,要在每一個(gè)獲取返回值的地方寫if判斷。 其實(shí),這個(gè)時(shí)候,使用raise來(lái)拋出一個(gè)異常,比用return返回標(biāo)志位(True或False),更加簡(jiǎn)單,代碼的可讀性和可維護(hù)性也更好,代碼的層次感也越強(qiáng)。
return語(yǔ)句只能返回到上一層調(diào)用的地方,如果調(diào)用層次比較多,底層的問(wèn)題,要層層傳遞上來(lái)就太費(fèi)勁了, 這樣代碼寫起來(lái)看起來(lái)都很別扭。return主要還是用來(lái)返回?cái)?shù)據(jù)的,而raise是更好的“返回異?!钡姆绞?。
在一個(gè)處于層層調(diào)用關(guān)系的流程中,不管哪個(gè)地方raise拋出一個(gè)異常,我們只需要在流程需要的地方try…except…捕獲異常, 就可以了。raise拋出異常后,代碼返回到最近的try…except…的地方(這是個(gè)與return很不一樣的細(xì)節(jié)),這樣中間流程的代碼, 寫起來(lái)就會(huì)很輕松愜意優(yōu)雅。而且,如果中間雖捕獲了異常,但是不對(duì)異常進(jìn)行處理,也可以直接獨(dú)立的依據(jù)raise,再次將異常拋出,交給更上層來(lái)處理。
舉個(gè)例子:
def level_1():
raise ValueError("this is a value error")
def level_2():
level_1()
print("in level 2")
def level_3():
level_2()
print("in level 3")
def top():
level_3()
print("in top")
try:
top()
except:
print("catch exception from level 1")
# 輸出:catch exception from level 1
以上示例代碼,在最底層的函數(shù)raise一個(gè)ValueError異常,top函數(shù)與直接raise異常的函數(shù),中間還經(jīng)過(guò)了兩層調(diào)用。 不過(guò),運(yùn)行程序發(fā)現(xiàn),最底層raise之后,在最頂層直接捕獲異常,而且,很重要的細(xì)節(jié)是,代碼中所有的打印都沒(méi)有執(zhí)行, 代碼相當(dāng)于從最底層直接return到了最頂層try…except…的地方。
這種代碼的寫法,比一層層return再判斷,要簡(jiǎn)單很多。這種層層調(diào)用在軟件中很常見(jiàn),稍微封裝一下底層接口代碼, 層次關(guān)系就出現(xiàn)了。如果再學(xué)會(huì)了自定義Python的異常類,配合這種寫法,您的代碼一定會(huì)更加漂亮性感!
單獨(dú)一句raise的作用
代碼中常常能看到單獨(dú)使用一句raise,后面不帶任何參數(shù),這樣寫的作用是,將向下文當(dāng)前的異常拋出(raise語(yǔ)句不帶參數(shù)的默認(rèn)動(dòng)作)。
def do_raise():
raise ValueError('test value error')
def middle():
try:
do_raise()
except:
print('something wrong')
raise
def top():
try:
middle()
except ValueError as e:
print(repr(e))
top()
在middle函數(shù)中,單獨(dú)使用raise語(yǔ)句,它將會(huì)被do_raise拋出的異常,直接在此拋出。middle函數(shù)不對(duì)此異常進(jìn)行處理,而是交給上層代碼去處理。這段代碼的運(yùn)行效果如下:
something wrong
ValueError('test value error')
raise在層層調(diào)用的代碼流程中,簡(jiǎn)化了異常處理的代碼編寫,并形成了自己獨(dú)有的異常處理層次關(guān)系,使得代碼在處理異常時(shí)非常靈活高效。
自定義異常
class MyException(BaseException): # BaseException是所有異常的基類
def __init__(self,msg):
self.message = msg
def __str__(self):
return self.message
try:
raise MyException("我的錯(cuò)誤")
except MyException as e:
print(e)
斷言
- assert語(yǔ)法用于判斷代碼是否符合執(zhí)行預(yù)期
- 一般來(lái)說(shuō)在做單元測(cè)試的時(shí)候用的比較多,在生產(chǎn)環(huán)境代碼運(yùn)行的情況下,不建議使用斷言,會(huì)讓程序abort掉。
assert type(1) is int
assert 1+1 == 2
assert 1+1 == 3 # 會(huì)報(bào)AssertionError
應(yīng)用場(chǎng)景舉例,別人調(diào)你的接口,你的接口要求他調(diào)用時(shí)必須傳遞指定的關(guān)鍵參數(shù),等他傳遞進(jìn)來(lái)時(shí),你就可以用用assert語(yǔ)句他傳的參數(shù)是否符合你的預(yù)期
def my_interface(name,age,score):
assert type(name) is str
assert type(age) is int
assert type(score) is float
my_interface("alex",22,89.2)
|