一、什么是异常

 

Python错误与异常

什么是异常

> 异常是一个事件,该事件会在程序执行过程中发生,影响程序的正常执行。一般情况下,在Python无法正常处理程序时就会发生异常。异常是Python的对象,表示一个错误。当Python脚本发生异常时,我们需要捕获并处理异常,否则程序会终止执行。

> 每一个异常都是一些类的实例,这些实例可以被引用,并且可以用很多种方法进行捕捉,使得错误可以被处理,而不是让整个程序失败。

代码里会有很多异常,比如 NameError 名称错误,Syntax Error 语法异常,Type Error 类型错误,Value Error值异常;这四种都是异常,异常其实是一个事件

代码里有异常是非常正常的事情,捕获到异常,扔掉就可以了

>>> 10*(1/0)

Traceback (most recent call last):

File "<stdin>", line 1, in <module>

ZeroDivisionError: division by zero

>>> 4+a1*3

Traceback (most recent call last):

File "<stdin>", line 1, in <module>

NameError: name 'a1' is not defined

>>> '2'+2

Traceback (most recent call last):

File "<stdin>", line 1, in <module>

TypeError: can only concatenate str (not "int") to str

这些就叫做异常

 

二、异常处理

 

异常处理

> try语句的基本形式为try/except。try/except语句用来检测try语句块中的错误,从而让except语句捕获异常信息并处理。如果你不想在发生异常时结束程序,只需在try语句块中捕获异常即可。

> 捕获异常的语法如下:

> 异常捕捉try/except

1-1def exp_exception(x,y):try:
        a=x/yprint('a=',a)return aexcept Exception:print("程序出现异常,异常信息:被除数为0")
 
exp_exception(2,2)=>a= 1.0
1-2def exp_exception(x,y):try:
        a=x/yprint('a=',a)return aexcept Exception:print("程序出现异常,异常信息:被除数为0")
 
exp_exception(2,0)=>程序出现异常,异常信息:被除数为0

 

> 捕捉多个异常

1-1def exp_exception(x,y):try:
        a=x/y
        b=nameprint('a=',a)return aexcept ZeroDivisionError:print("除数不能为0")except NameError:print("没有你要找的名字")
 
exp_exception(2,0)=>除数不能为0
1-2def exp_exception(x,y):try:
        a=x/y
        b=nameprint('a=',a)return aexcept ZeroDivisionError:print("除数不能为0")except NameError:print("没有你要找的名字")
 
exp_exception(2,2)=>没有你要找的名字

 

> 使用一个块捕捉多个异常

> 如果需要使用一个块捕捉多个类型异常,可以将它们作为元组列出。使用该方式时,遇到的异常类型是元组中的任意一个,都会走异常流程。

> 这么做有什么好处呢?假如我们希望多个except子句输出同样的信息,就没有必要在几个except子句中重复输入语句,放到一个异常块中即可。

刚刚使用 except 捕获了两个异常,现在想把两个异常写到一个except中

def exp_exception(x,y):try:
        a=x/y
        b=nameprint('a=',a)return aexcept (ZeroDivisionError,NameError,TypeError):print("你的输出数据有误!")
 
exp_exception(2,'0')=>你的输出数据有误!

 

异常处理

> 捕捉对象

> 如果希望在except子句中访问异常对象本身,也就是看到一个异常对象真正的异常信息,而不是输出自己定义的异常信息,可以使用as e的形式,我们称之为捕捉对象。

def exp_exception(x,y):try:
        a=x/y
        b=nameprint('a=',a)return aexcept (ZeroDivisionError,NameError,TypeError) as e:print(e)    # 输出 e,这地方不要写自己自定义的内容 exp_exception(2,'0')
exp_exception(2,0)
exp_exception(2,2)=>unsupported operand type(s) for /: 'int' and 'str'division by zero
name 'name' is not defined

这就是捕捉对象,前面的异常提示是自定义的,捕捉对象的意思是系统给的系统提示,用 as e 即可

用系统给出的提示相比更便于定位

 

> 全捕捉

> 在实际编码过程中,即使程序能处理好几种类型的异常,但有一些异常还是会从我们手掌中溜走。对于这种情况我们根本无法预测会发生什么,也无法提前做任何准备。在这种情况下,与其使用不是捕捉异常的try/except语句隐藏异常,不如让程序立即崩溃。

def exp_exception(x,y):try:
        a=x/y
        b=nameexcept (ZeroDivisionError,NameError,TypeError) as e:print(e)   
 
exp_exception(2,'')=>unsupported operand type(s) for /: 'int' and 'str'

这样b=name的异常就逃走了,没有对 b 进行检查

对于这种情况,无法预测未来会发生什么,也没办法提前做任何准备,所以缺陷一定会产生,所以这时不如让异常直接崩溃,直接在后面什么都不写就可以了

def exp_exception(x,y):try:
        a=x/y
        b=nameexcept:print("Error")   
 
exp_exception(2,'')=>Error

后面什么都不写就叫全捕捉,当然这只是一种解决方案,从实用性角度看不建议这样做,因为这样捕捉异常非常危险,会隐藏所有没有预先想到的错误

 

> try/except...else

> try/except 语句还有一个可选的 else 子句,如果使用这个子句,那么必须放在所有的 except 子句之后。

> else 子句将在 try 子句没有发生任何异常的时候执行。

> try/except...else

def exp_exception(x,y):try:
        a=x/yexcept:print("Error")   else:print("程序没有错误,执行结束")
 
exp_exception(2,'')
exp_exception(2,2)=>Error

程序没有错误,执行结束

当程序没有异常时,会执行 else 子句流程

 

> try-finally 语句

> try-finally 语句无论是否发生异常都将执行最后的代码。

> 在有finally的异常处理程序中,finally中的子句一定是最后执行的。finally子句在关闭文件或数据库连接时非常有用

如果有异常,try => except => finally

如果无异常,try => else => finally

def use_finally(x,y):try:
        a=x/yfinally:print("不管有没有异常,都会执行我的哦~")
 
use_finally(2,2)
use_finally(2,0)=>不管有没有异常,都会执行我的哦~不管有没有异常,都会执行我的哦~Traceback (most recent call last):
 File "d:/VSCode/Untitled-1.py", line 10, in <module>
 use_finally(2,0)
 File "d:/VSCode/Untitled-1.py", line 5, in use_finally
 a=x/y
ZeroDivisionError: division by zero

但这引起了一个新的问题,虽然执行了 finally 语句,但是还是抛出异常了

能不能用 except 在 try 里面解惑它呢

def use_finally(x,y):try:
        a=x/yexcept ZeroDivisionError:print("除数不能为0")finally:print("不管有没有异常,都会执行我的哦~")
 
use_finally(2,2)
use_finally(2,0)=>不管有没有异常,都会执行我的哦~除数不能为0
不管有没有异常,都会执行我的哦~

现在加上else也是一样可以的

def use_finally(x,y):try:
        a=x/yexcept ZeroDivisionError:print("除数不能为0")else:print("程序执行成功")finally:print("不管有没有异常,都会执行我的哦~")
 
use_finally(2,2)
use_finally(2,0)=>程序执行成功
不管有没有异常,都会执行我的哦~除数不能为0
不管有没有异常,都会执行我的哦~

除了 try,后面的 except else 和 finally 都被称为 try 的子句,必须和 try 配合使用才有意义

 

三、抛出异常

 

抛出异常

> Python 使用 raise 语句抛出一个指定的异常。

> raise语法格式如下:

前面一直在说捕获异常,异常必须是能够抛出来才能捕获的,python中使用 raise 抛出指定的异常

使用 raise 触发异常,把异常引出来即可,用实例调用 raise 语句,引发异常

>>> raise Exception

Traceback (most recent call last):

File "<stdin>", line 1, in <module>

Exception

>>> raise NameError("This is NameError")

Traceback (most recent call last):

File "<stdin>", line 1, in <module>

NameError: This is NameError

通过 这两个实例,可以看出,第一个实例引发没有相关错误信息的普通异常

第二个输出了一些错误提示;

如果只想知道有没有抛出异常,并不想处理它,使用一个 raise 就可以把异常抛出

try:raise NameError("这是一个NameError")except NameError:print("捕捉到NameError") # 不加 raise,输出对应字符就结束=>捕捉到NameError
try:raise NameError("这是一个NameError")except NameError:print("捕捉到NameError")raise=>捕捉到NameError
Traceback (most recent call last):
 File "d:/VSCode/Untitled-1.py", line 4, in <module> raise NameError("这是一个NameError")
NameError: 这是一个NameError

这样又把 NameError异常抛出来了,raise可以抛出更深更详尽的异常信息

 

Python重要的内建异常类

Exception:常规错误的基类

AttributeError:对象没有这个属性

IOError:输入/输出操作失败

IndexError:序列中没有此索引

KeyError:映射中没有这个键

NameError:未声明/初始化对象(没有属性)

SyntaxError:python语法错误

SystemError:一般解释器系统错误

ValueError:传入无效的参数