异常可分为两大类:系统自己抛出的异常和我们主动抛出的异常。
1.主动抛出异常(raise语句)
raise语句:使用raise语句可以直接抛出异常,其可以使用一个类(必须是Exception类或Exception类的子类)或异常对象抛出异常。
如果使用类系统会自动创建类的实例。
raise Exception
Python语句中内置了很多异常类,下表是一些重要的内建异常类。
异常类名 | 描述 |
---|---|
Exception | 所有异常的基类 |
AttributeError | 属性引用或赋值失败时抛出的异常 |
OSError | 当操作系统无法执行任务时抛出的异常 |
IndexError | 在使用序列中不存在索引时抛出的异常 |
KeyError | 在使用映射中不存在的键值时抛出的异常 |
NameError | 再找不到名字(变量)时抛出的异常 |
SyntaxError | 在代码为错误形式时触发 |
TypeError | 在内建操作或函数应用于错误类型的对象时抛出的异常 |
ValueError | 在内建操作或者函数应用于正确类型的对象,但该对象使用了不适合的值时抛出的异常 |
ZeroDivisionError | 在除法或者取模操作的第二个参数值为0时抛出的异常 |
我们还可以为异常信息加上一个描述。
raise Exception("这是自己主动抛出的一个异常")
raise ArithmeticError("这是一个跟数值有关的异常")
2.自定义异常类
任何一个异常类必须是Exception的子类。最简单的自定义异常类就是一个空的Exception类的子类。
class MyException(Exception):
pass
a=20
if a>15:
raise MyException("a数值大于15")
3.捕捉异常
try…except语句
python中使用try…except语句进行异常捕捉。(如果异常未捕捉,系统会一直将异常传递下去,直到程序由于异常而导致中断。所以为了避免这种情况,需要对可能导致程序中断崩溃的代码段进行异常捕捉。)
try和except之间是正常执行的语句,如果这些代码不发生错误,那么就会正常执行下去,而except部分的代码就不会执行;如果try和except之间的代码发生了错误,那么错误点后面的代码就不会被执行了,而是会跳到except去执行except代码块中的代码。
x=None
while True:
try:
if x==None:
x=int(input("输入分子:"))
y=int(input("输入分母:"))
print("x/y={}".format(x/y))
break;
except:
print("分母不能为0,请重新输入分母!")
输入分子:5
输入分母:0
分母不能为0,请重新输入分母!
输入分母:2
x/y=2.5
Process finished with exit code 0
捕捉多个异常
except子句可以包含任意多个,所以我们可以使用多个不同的具体异常类来捕捉已知可能出现的几种异常,为防止遗漏某个异常,我们还可以在最后一个except子句不使用任何异常类,这样它就会捕捉其他所有未指定的异常,从而让程序更加健壮。
class NegativeException(Exception):
pass
class ZeroException(Exception):
pass
class SpecialCalc:
def add(self,x,y):
if x<0 or y<0:
raise NegativeException
return x+y
def sub(self,x,y):
if x-y<0:
raise NegativeException
return x-y
def mul(self,x,y):
if x==0 or y==0:
raise ZeroException
return x*y
def div(self,x,y):
return x/y
while True:
try:
calc=SpecialCalc()
expr=input("请输入要计算的表达式:")
if expr=="exit":
break;
result=eval('calc.'+expr)
print("计算结果:{:.2f}".format(result))
except NegativeException:
print("负数异常")
except ZeroException:
print("操作数为0异常")
except ZeroDivisionError:
print("分母不能为0")
except:
print("其他异常")
请输入要计算的表达式:add(5,6)
计算结果:11.00
请输入要计算的表达式:add(5,-3)
负数异常
请输入要计算的表达式:sub(3,6)
负数异常
请输入要计算的表达式:div(5,0)
分母不能为0
请输入要计算的表达式:mul(0,3)
操作数为0异常
请输入要计算的表达式:abcd
其他异常
请输入要计算的表达式:exit
Process finished with exit code 0
还可以用同一个代码块处理多个异常,如下
try:
...
except(异常类1,异常类2,异常类3,...,异常类n):
...
捕捉对象
所有抛出的异常,其实都是异常对象。
上面那份代码是根据异常类来输出异常信息的,不过这存在一个问题,比如add和sub操作抛出的都是属于NegativeException异常类的异常对象,同样都输出”负数异常“信息,尽管抛出的异常对象属同个异常类,但这两个异常对象却存在差异:add方法是由于操作数存在负数导致的,sub方法是由于两个操作数的运行结果是负数导致的。
所以可为这些异常指定一个异常对象变量(用到as关键字),当输出异常对象时,就会输出相应的异常信息。
class NegativeException(Exception):
pass
class ZeroException(Exception):
pass
class SpecialCalc:
def add(self,x,y):
if x<0 or y<0:
raise NegativeException("x和y都不能小于0")
return x+y
def sub(self,x,y):
if x-y<0:
raise NegativeException("x与y的差值不能小于0")
return x-y
def mul(self,x,y):
if x==0 or y==0:
raise ZeroException("x和y都不能等于0")
return x*y
def div(self,x,y):
return x/y
while True:
try:
calc=SpecialCalc()
expr=input("请输入要计算的表达式:")
if expr=="exit":
break;
result=eval('calc.'+expr)
print("计算结果:{:.2f}".format(result))
except (NegativeException,ZeroException) as e:
print(e)
except ZeroDivisionError as e:
print(e)
except:
print("其他异常")
请输入要计算的表达式:add(5,-3)
x和y都不能小于0
请输入要计算的表达式:sub(3,6)
x与y的差值不能小于0
请输入要计算的表达式:mul(0,4)
x和y都不能等于0
请输入要计算的表达式:div(5,0)
division by zero
请输入要计算的表达式:exit
Process finished with exit code 0
else子句
else子句会在try和except之间的代码正常执行后才执行。
try:
...
except:
#抛出异常时执行这段代码
else:
#正常执行后执行这段代码
while True:
try:
x=int(input("输入分子:"))
y=int(input("输入分母:"))
print("x/y={}".format(x/y))
except Exception as e:
print("错误输入:",e,",请重新输入!")
else:
break
输入分子:30
输入分母:0
错误输入: division by zero ,请重新输入!
输入分子:30
输入分母:12
x/y=2.5
Process finished with exit code 0
finally子句
不管正常执行还是抛出异常,最后都会执行finally子句中的代码。所以应该在finally子句中放置关闭资源的代码,如关闭文件、关闭数据库等。
try:
...
except:
...
finally:
#无论是否抛出异常都会执行这段代码
如果使用return语句退出函数,也一定会先去执行完finally子句中的代码再退出函数。
def fun1():
try:
return 20
except:
print("fun1抛出异常")
finally:
print("fun1 finally")
print(fun1())
#Output:
fun1 finally
20
def fun2():
try:
x=1/0 #分母为0,抛出异常,x变量创建失败
except ZeroDivisionError as e:
print(e)
finally:
print("fun2 finally")
try:
del x #del语句删除一个创建失败(即不存在)的x变量,又抛出异常
except Exception as e:
print(e)
fun2()
#Output:
division by zero
fun2 finally
local variable 'x' referenced before assignment
try语句中,try是必需的,except和finally必须至少要有一个,else子句可有可无。
4.异常的妙用
例如,在字典中通过key获取value时,或在访问对象中的属性和方法时,我们经常需要使用if语句先判断是否存在,防止因不存在而抛出异常。
不过在代码中充斥太多的if语句会降低代码的可读性,因此可以用try语句来取代if语句,并让程序更加健壮。
dict={'a':1,'b':2}
if 'A' in dict:
print(dict['A'])
#可用try语句取代if语句
try:
print(dict['A'])
except KeyError as e:
print("异常信息:{}".format(e)) #异常信息:'A'
class QWE():
def test(self):
print("test")
m=QWE()
if hasattr(m,"Test"):
m.Test()
#可用try语句取代if语句:
try:
m.Test()
except AttributeError as e:
print("异常信息:{}".format(e)) #异常信息:'QWE' object has no attribute 'Test'