一、异常的简介
1.异常定义
程序在运行过程中不可避免会出现一些错误,比如使用了没有被赋值过的变量、除0、使用了不存在的索引等等。
如执行print(a)
,会抛出NameError: name 'a' is not defined
;
执行print(1/0)
,会抛出ZeroDivisionError: division by zero
。
这些错误在程序中即称为异常。
程序在运行过程中一旦出现异常会导致程序立即终止,异常后面的代码都不会执行。
2.处理异常
程序出现异常,目的不是要程序立即终止;
Python中设置异常的目的时再出现异常时,编写代码对异常进行处理。
处理异常语法:try_except_else语句
try:
代码块(可能出现错误的语句)
except:
代码块(出现错误以后的处理方式)
else:
代码块(没有错误时要执行的语句)
如,在try代码块中没有错误时,
print('Python')
try:
print(1/1)
except:
print('Error in Try')
print('hello')
结果为,
Python
1.0
hello
在try代码块中有错误时
print('Python')
try:
print(1/0)
except:
print('Error in Try')
print('hello')
打印
Python
Error in Try
hello
后边有else语句时且try代码块中没有错误时,
print('Python')
try:
print(1/1)
except:
print('Error in Try')
else:
print('No error in Try')
print('hello')
结果为
Python
1.0
No error in Try
hello
后边有else语句时且try代码块中有错误时,
print('Python')
try:
print(1/0)
except:
print('Error in Try')
else:
print('No error in Try')
print('hello')
打印
Python
Error in Try
hello
二、异常的传播
当在函数中出现异常时,如果在函数中对异常进行了处理,则异常不会再传播;
如果函数中没有对异常进行处理,则异常继续向函数调用处传播。
如,
def fn():
print('hello.......')
print(1/0)
fn()
会出现两次报错,
Traceback (most recent call last):
File "xxx/Demo.py", line 16, in <module>
fn()
File "xxx/Demo.py", line 14, in fn
print(1 / 0)
ZeroDivisionError: division by zero
对异常进行处理后,
def fn():
print('hello.......')
try:
print(1 / 0)
except:
pass
fn()
打印hello.......
。
如果函数调用处处理了异常,则不再传播;
如果没有处理则继续向调用处传播;
直到传递到全局作用域,如果依然没有处理,则程序终止,并显示异常信息。
def fn():
print('in fn')
print(1/0)
def fn2():
print('in fn2')
fn()
def fn3():
print('in fn3')
fn2()
fn3()
打印
in fn3
in fn2
in fn
Traceback (most recent call last):
File "xxx/Demo.py", line 34, in <module>
fn3()
File "xxx/Demo.py", line 32, in fn3
fn2()
File "xxx/Demo.py", line 28, in fn2
fn()
File "xxx/Demo.py", line 24, in fn
print(1/0)
ZeroDivisionError: division by zero
即抛出了ZeroDivisionError,这是一个异常对象。
三、异常对象
当程序运行过程中出现异常以后,所有的异常信息会被专门保存到一个异常对象中;
异常传播时,实际上就是异常对象抛给了调用处。
如NameError类专门处理变量的错误。
print('Before Exception')
try:
print(a)
print(1/0)
except:
print('Handling Exception')
print('After Exception')
打印结果为
Before Exception
Handling Exception
After Exception
except后增加异常对象时,
print('Before Exception')
try:
print(a)
print(1/0)
except NameError:
print('NameError Exception')
print('After Exception')
结果为
Before Exception
NameError Exception
After Exception
但是当except语句中的异常对象未与try语句块中的异常类型匹配时,
print('Before Exception')
try:
print(1/0)
except NameError:
print('NameError Exception')
print('After Exception')
打印
Traceback (most recent call last):
File "xxx/Demo.py", line 37, in <module>
print(1/0)
ZeroDivisionError: division by zero
出现报错,意即except后的异常对象只能捕获与之对应的异常,不能捕获其他异常。
print('Before Exception')
try:
print(1/0)
except NameError:
print('NameError Exception')
except ZeroDivisionError:
print('ZeroDivisionError Exception')
print('After Exception')
打印
Before Exception
ZeroDivisionError Exception
After Exception
即此时捕获到ZeroDivisionError
,try语句块后边可以同时跟多个except语句块,来捕获多个异常。
如果except后面不跟任何内容,此时会捕获到所有的异常;
如果except后面跟着一个异常类型,此时只会捕获该类型的异常。
Exception是所有异常类型的父类;
如果except后跟的是Exception,它会捕获到所有的异常。
print('Before Exception')
try:
print(a)
print(1/0)
except Exception:
print('in Exception')
print('After Exception')
结果为
Before Exception
in Exception
After Exception
即Exception可以捕获所有的异常,并且可将异常对象赋值给另一个对象,来进行打印以识别异常类型。
print('Before Exception')
try:
print(1/0)
except Exception as e:
print(e,type(e))
print('After Exception')
打印
Before Exception
division by zero <class 'ZeroDivisionError'>
After Exception
可据此得出异常内容和异常类型。
finally语句
无论是否出现异常都会执行。
没有异常时,
print('Before Exception')
try:
print(1/1)
finally:
print('Hello')
print('After Exception')
打印结果为,
Before Exception
1.0
Hello
After Exception
有异常但无except语句块时,
print('Before Exception')
try:
print(1/0)
finally:
print('Hello')
print('After Exception')
打印结果为,
Traceback (most recent call last):
File "xxx/Demo.py", line 37, in <module>
print(1/0)
ZeroDivisionError: division by zero
Before Exception
Hello
有异常且有except语句块时,
print('Before Exception')
try:
print(1/0)
except:
print('In Exception')
finally:
print('Hello')
print('After Exception')
打印
Before Exception
In Exception
Hello
After Exception
显然,有无异常时均执行了finally语句块中的语句。
当try语句块中有异常时,如果没有except语句,先执行finally语句块,再回到try语句块报错;
如果有except语句块,则先执行except语句块,再执行finally语句块。
异常的完整语法:
try:
代码块(可能出现错误的语句)
except 异常类型1 as 异常名:
代码块(出现错误以后的处理方式)
except 异常类型2 as 异常名:
代码块(出现错误以后的处理方式)
except 异常类型3 as 异常名:
代码块(出现错误以后的处理方式)
...
else:
代码块(没有错误时要执行的语句)
finally:
代码块(是否有异常都会执行)
四、自定义异常对象
1.抛出异常
使用raisre语句来抛出异常,后面需要跟一个异常类或异常实例。
def add(a,b):
r = a + b
return r
print(add(-1,2))
打印结果为1
。
现在定义函数只计算正数,如果a、b中有负数,就向调用处抛异常。
def add(a,b):
if a < 0 or b < 0:
raise Exception
r = a + b
return r
print(add(-1,2))
此时会报错
Traceback (most recent call last):
File "xxx/Demo.py", line 48, in <module>
print(add(-1,2))
File "xxx/Demo.py", line 44, in add
raise Exception
Exception
同时,可自定义异常类型,便于直观得出异常信息
def add(a,b):
if a < 0 or b < 0:
raise Exception('NegativeError')
r = a + b
return r
print(add(-1,2))
此时为
Traceback (most recent call last):
File "xxx/Demo.py", line 48, in <module>
print(add(-1,2))
File "xxx/Demo.py", line 44, in add
raise Exception('NegativeError')
Exception: NegativeError
抛出异常的目的,时告诉调用者调用时可能出现问题,以便自己进行处理。
2.定义异常类
只需要继承Exception,就可以了。
class MyError(Exception):
pass
def add(a,b):
if a < 0 or b < 0:
raise MyError('NegativeError')
r = a + b
return r
print(add(-1,2))
结果为
Traceback (most recent call last):
File "xxx/Demo.py", line 53, in <module>
print(add(-1,2))
File "xxx/Demo.py", line 49, in add
raise MyError('NegativeError')
__main__.MyError: NegativeError
抛出了MyError异常,并给出了异常提示信息。
大家也可以关注我的公众号:Python极客社区,在我的公众号里,经常会分享很多Python的文章,而且也分享了很多工具、学习资源等。另外回复“电子书”还可以获取十本我精心收集的Python电子书。