今天我們來說說Python的異常處理,在說異常處理之前我們先來了解下寄猩,Python中有哪些常見的錯(cuò)誤類型嫉晶。
在Python中,當(dāng)代碼中有錯(cuò)誤時(shí)田篇,程序默認(rèn)會在終端中將錯(cuò)誤類型和原因打印出來:
以下是Python中一些常用錯(cuò)誤類型:
- SyntaxError(語法錯(cuò)誤)
print('函數(shù)最后沒有加小括號'
#運(yùn)行程序后報(bào)錯(cuò) SyntaxError: unexpected EOF while parsing
- NameError(名稱錯(cuò)誤)
if not_defined > 0:
print("NOk")
#運(yùn)行程序后報(bào)錯(cuò) NameError: name 'not_defined' is not defined
- LookupError (查詢錯(cuò)誤)
li = [1, 2, 3]
li[1024]
#運(yùn)行程序后報(bào)錯(cuò) IndexError: list index out of range
di = {'code':'JD', 'price':42}
di['CEO']
#運(yùn)行程序后報(bào)錯(cuò) KeyError: 'CEO'
- ArithmeticError(運(yùn)算錯(cuò)誤)
1/0
#運(yùn)行程序后報(bào)錯(cuò) ZeroDivisionError: division by zero
- TypeError(類型錯(cuò)誤)
days = 365 + '1004'
#運(yùn)行程序后報(bào)錯(cuò) TypeError: unsupported operand type(s) for +: 'int' and 'str'
- ValueError(值錯(cuò)誤)
int('永遠(yuǎn)有多遠(yuǎn)')
#運(yùn)行程序后報(bào)錯(cuò) ValueError: invalid literal for int() with base 10: '永遠(yuǎn)有多遠(yuǎn)'
- AttributeError(屬性錯(cuò)誤)
a = 1
a.append(2)
#運(yùn)行程序后報(bào)錯(cuò) AttributeError: 'int' object has no attribute 'append'
- OSError(系統(tǒng)錯(cuò)誤)
f = open('No Such File.txt')
#運(yùn)行程序后報(bào)錯(cuò) FileNotFoundError: [Errno 2] No such file or directory: 'No Such File.txt'
- ImportError(導(dǎo)入錯(cuò)誤)
from numpy import sqr
#運(yùn)行程序后報(bào)錯(cuò) ImportError: cannot import name 'sqr' from 'numpy'
- ModuleNotFoundError(未找到模塊錯(cuò)誤)
import numq
#運(yùn)行程序后報(bào)錯(cuò) ModuleNotFoundError: No module named 'numq'
- StopIteration(迭代停止錯(cuò)誤)
i = iter([1,2,3])
print(next(i))
print(next(i))
print(next(i))
print(next(i))
#運(yùn)行程序后報(bào)錯(cuò) StopIteration:
看到這里替废,你一定會有一個(gè)疑問,Python怎么有那么多不同的錯(cuò)誤分類呀泊柬,我都需要一一全都記下來嗎椎镣?
就個(gè)人學(xué)習(xí)的經(jīng)驗(yàn)來說,可以先以了解為主兽赁,在后續(xù)實(shí)際應(yīng)用中状答,記住與自己代碼相關(guān)的錯(cuò)誤類型即可冷守。
隨后,我們來說說今天的主題剪况,異常處理教沾,我們首先需要明白的是,我們無法完全阻止錯(cuò)誤發(fā)生译断,但是可以提前預(yù)防以至于程序不會崩潰授翻。這個(gè)提前預(yù)防的動(dòng)作稱為異常處理(exception handling),異常處理就是為了防患于未然孙咪。
從以上的錯(cuò)誤類型我們知道堪唐,Python 在程序報(bào)錯(cuò)時(shí)會返回錯(cuò)誤類型和信息,我們可以提前去捕捉我們能預(yù)計(jì)到的及無法預(yù)計(jì)到的錯(cuò)誤類型翎蹈,并做相應(yīng)的處理淮菠。
接下來,我們來介紹幾種典型的異常處理方式:
1. Try-Except
異常處理最常見的語句就是 try-except 組合荤堪,細(xì)分又有三種類型:
(1) 知道錯(cuò)誤但不確定類型合陵,用 except Exception
例子:
def divide(a, b):
try:
c = a / b
print(f"Result = {c:.4f}.")
except Exception as err:
print(f'因子是0,無法進(jìn)行相除! 具體錯(cuò)誤為:{err}')
divide(10, 0) #因子是0澄阳,無法進(jìn)行相除! 具體錯(cuò)誤為:division by zero
這里解釋下:
except Exception as err
的含義是將任意異常定義為一個(gè)err對象拥知,后續(xù)可將err轉(zhuǎn)為字符串打印出來,程序會打印錯(cuò)誤原因碎赢,如需同時(shí)打印錯(cuò)誤類型和原因低剔,則需要使用repr(err)。
(2)知道錯(cuò)誤而且確定類型肮塞,用 except some_exception
例子:
def divide(a, b):
try:
c = a / b
print(f"Result = {c:.4f}.")
except ZeroDivisionError:
print(f'因子是0襟齿,無法進(jìn)行相除! ')
divide(10, 0) #因子是0,無法進(jìn)行相除!
(3)知道錯(cuò)誤而且有多個(gè)錯(cuò)誤
用多個(gè) except
或
用 except (exc_1, exc_2, ... exc_n)
例子:
def divide(a, b):
try:
c = a / b
d = cc + 1
print(f"Result = {c:.4f}.")
except ZeroDivisionError:
print('因子是0枕赵,無法進(jìn)行相除!')
except NameError:
print('變量名未定義猜欺!')
divide(10, 2) #變量名未定義!
divide(10, 0) #因子是0拷窜,無法進(jìn)行相除!
有時(shí)替梨,我們也可以對多個(gè)錯(cuò)誤進(jìn)行統(tǒng)一處理,比如:
except (ZeroDivisionError装黑,NameError):
print('Error occurred!')
實(shí)際工作中弓熏,我們也會遇到不同錯(cuò)誤類型恋谭,做差分處理的情況,比如控制機(jī)械手臂時(shí)挽鞠,如遇到sockets連接丟失的異常時(shí)疚颊,可以做重連操作狈孔,遇到控制指令返回超時(shí)的異常可以做提示重試的處理材义,其他任意錯(cuò)誤則終止機(jī)械手臂的控制均抽,這樣代碼既智能又安全。
try:
動(dòng)作
except SocketsError:
重連處理
except NoResponseError:
提示重試處理
except Exception:
停止控制
p.s 以上SocketsError和NoResponseError為自建的錯(cuò)誤類型其掂,Exception為所有其他異常即所有其他錯(cuò)誤類型油挥,程序出錯(cuò)時(shí)會進(jìn)入對應(yīng)的一種錯(cuò)誤類型中去。
2. Try-Except-Else
首先要明確的是款熬,else 語句是可有可無的深寥。如果存在,則 else 語句應(yīng)始終在 except 語句之后贤牛。
當(dāng) try 語句下的代碼未發(fā)生異常時(shí)惋鹅,才會執(zhí)行 else 子句下的代碼。
當(dāng) try 語句下的代碼中發(fā)生異常殉簸,則 except 語句將處理異常闰集,else 語句將不會執(zhí)行。
個(gè)人認(rèn)為般卑,Try-Except-Else的存在感并不是很強(qiáng)武鲁,因?yàn)橥耆梢酝ㄟ^在try的最后,添加相應(yīng)的代碼來實(shí)現(xiàn)同樣的功能椭微。
3. Try-Except-Else-Finally
無論是否發(fā)生異常洞坑,finally 語句始終在 try 語句運(yùn)行之前執(zhí)行。
在實(shí)際工作中蝇率,finally 是非常有用的迟杂。
比如調(diào)用音頻采集卡時(shí),我們希望無論采集成功或異常失敗時(shí)本慕,最終程序都會做一次停止采集的動(dòng)作來釋放資源排拷。這時(shí)我們就可以將停止采集的動(dòng)作放在finally語句中。
4. Raise Exception
除了上面處理異常的操作之外锅尘,我們還可以用 raise 關(guān)鍵詞“拋出”異常监氢,拋出異常可分為兩類:
(1) 拋出內(nèi)置異常
在下例中藤违,如果輸入非整數(shù)浪腐,我們拋出一個(gè) ValueError(注意這是 Python 里面內(nèi)置的異常對象),順帶打印“This is not a positive number”的信息顿乒。
try:
a = int(input("Enter a positive integer: "))
if a <= 0:
raise ValueError("That is not a positive number!")
except ValueError as err:
print(err)
(2) 拋出自定義異常
這里我們先需要了解如何自定義異常:
在 Python 里议街,所有異常都是 Exception 的子類,因此在定義其類時(shí)需要進(jìn)行繼承璧榄。
class Error(Exception):
class your_exception(Error):
具體代碼如下特漩。
class Error(Exception):
pass
class NegativePortfolioValueWarning(Error):
def __init__(self, expression, message):
self.expression = expression
self.message = message
隨后吧雹,我們就可以在代碼中,拋出自定義的NegativePortfolioValueWarning異常了涂身。
def portfolio_value(last_worth, current_worth):
if last_worth < 0 or current_worth < 0:
raise ValueError('Negative worth!')
value = current_worth - last_worth
if value < 0:
raise NegativePortfolioValueWarning(
'NegativePortfolioValueWarning', \
'Negative return. Take a look!')
總結(jié)雄卷,Python中對不同錯(cuò)誤類型做了細(xì)分且分類非常多,實(shí)際代碼調(diào)試過程中蛤售,我們可以通過打印的方式將錯(cuò)誤類型和原因打印出來丁鹉,以便讓程序繼續(xù)運(yùn)行的前提下了解錯(cuò)誤信息。
對于不同的錯(cuò)誤類型悍抑,我們可以做差分處理鳄炉,以便讓程序容錯(cuò)性更好且更智能。
對于自己編寫程序的一些特殊異常搜骡。我們可以自建錯(cuò)誤類型拂盯,并通過raise拋出。