Python 异常是指在程序运行中所产生的错误,即代码在无法正常执行的情况下就会产生异常。这个错误可以是Python内置的错误类型,也可以是开发者自定义的错误类型。异常即一个事件,异常会影响程序的执行流程,若没有用正确的方式捕获产生的异常,代码就会终止运行。
举一些Python常见的发生异常的场景:
比如将一个字母转换为整型。这时Python会自动抛出TypeError异常。
>>> int("python")
Traceback (most recent call last):
File "", line 1, in
ValueError: invalid literal for int() with base 10: 'python'
在比如从字典中取一个不存在的键,Python会自动抛出KeyError异常。
>>> dict_demo = {}
>>> key_demo = dict_demo["fun"]
Traceback (most recent call last):
File "", line 1, in
KeyError: 'fun'
这里出现的TypeError和KeyError都是Python内置的异常类型,它们都继承自父类Exception。
情况还有很多,这里不一一举例了。
Python允许定义自定义的错误类型。下面来定义一个自定义异常DemoError:
class DemoError(Exception):
def __init__(self, message):
self.message = "DemoError: " + message
def __str__(self):
return self.message
使用raise可以抛出异常。
# 抛出异常:
raise Exception("This is a Exception.")
# 抛出指定异常,可以是Python内置的异常类型,也可以是自定义的异常类型
# 抛出内置异常
raise TypeError("This is a TypeError.")
# 抛出自定义的异常
raise DemoError("This is my Exception ---- DemoError.")
在Python执行过程中,遇到raise抛出异常时,会跳出当前语句块,寻找捕获该异常的语句。如果代码中没有对抛出的异常进行捕获,程序就会报错。
class DemoError(Exception):
def __init__(self, message):
self.message = "DemoError: " + message
def __str__(self):
return self.message
if __name__ == "__main__":
print("I will raise a Exception in this process.")
raise DemoError("Just a test.")
print("After raise Exception...")
上面的代码中运行抛出DemoError,运行结果如下:
I will raise a Exception in this process.
Traceback (most recent call last):
File "/Users/my/Desktop/Python Apps/untitled_test/test4.py", line 12, in
raise DemoError("Just a test.")
__main__.DemoError: DemoError: Just a test.
可以看到raise下面的语句没有打印。因为代码中没有捕获抛出的异常,所以程序执行到12行报错。
对于Python内置的异常,通常会在使用不正确的Python语法时会自动抛出。例如运行Python脚本中包含错误的Python语法时会抛出SyntaxError。
>>> a = 5
>>> if a < 3
File "", line 1
if a < 3
^
SyntaxError: invalid syntax
我们也可以自己主动抛出内置的异常类型,并加上自己想要的错误描述:
>>> raise SyntaxError("Nothing...")
Traceback (most recent call last):
File "", line 1, in
SyntaxError: Nothing...
下面的一节内容会告诉大家怎么捕获异常(即异常怎么处理)。
1. 捕获异常
在Python中使用try-except来捕获异常。
except用来捕获try块中抛出的异常。
class DemoError(Exception):
def __init__(self, message):
self.message = "DemoError: " + message
def __str__(self):
return self.message
if __name__ == "__main__":
try:
print("I will raise a Exception in this process.")
raise DemoError("Just a test.")
print("After raise Exception...")
except:
print("In except..")
except捕获任何异常类型。当异常被捕获时,代码的执行就会进入except块中。上面代码的执行情况如下:
I will raise a Exception in this process.
In except..
使用except Exception与上面的捕获类似,功能依旧是捕获任何异常类型。不过可以对异常对象做出一些处理,比如打印出异常中包含的信息:
class DemoError(Exception):
def __init__(self, message):
self.message = "DemoError: " + message
def __str__(self):
return self.message
if __name__ == "__main__":
try:
print("I will raise a Exception in this process.")
raise DemoError("Just a test.")
except Exception as e:
print("In except..")
print(str(e))
代码的执行结果如下:
I will raise a Exception in this process.
In except..
DemoError: Just a test.
注意输出的最后一行,print(str(e))将异常中包含的错误信息打印了出来。
但是,使用类似except Exception这样的语句捕获任何异常也会有一些问题:
这样处理异常的方式有些泛化,类似于直接输出发生了一个错误。大多数情况下,代码还是要对捕获到的不同异常做出不同的应对。
使用 except 异常类型名称可以捕获特定类型的异常。
例如下面的代码:
class DemoError(Exception):
def __init__(self, message):
self.message = "DemoError: " + message
def __str__(self):
return self.message
if __name__ == "__main__":
try:
print("I will raise a Exception in this process.")
raise DemoError("Just a test.")
except DemoError as e:
print("It's a DemoError!")
print(str(e))
上面的代码中仅仅捕获了DemoError,执行结果如下:
I will raise a Exception in this process.
It's a DemoError!
DemoError: Just a test.
如果我将上面的代码换成其他异常,比如KeyError:
class DemoError(Exception):
def __init__(self, message):
self.message = "DemoError: " + message
def __str__(self):
return self.message
if __name__ == "__main__":
try:
print("I will raise a Exception in this process.")
raise KeyError("Just a test.")
except DemoError as e:
print("It's a DemoError!")
print(str(e))
代码执行就会报错:
Traceback (most recent call last):
File "/Users/my/Desktop/Python Apps/untitled_test/test4.py", line 13, in
raise KeyError("Just a test.")
KeyError: 'Just a test.'
I will raise a Exception in this process.
因为代码中没有对KeyError捕获的代码块,因此也就无法处理这种异常。
现在我们来修复这个bug:
class DemoError(Exception):
def __init__(self, message):
self.message = "DemoError: " + message
def __str__(self):
return self.message
if __name__ == "__main__":
try:
print("I will raise a Exception in this process.")
raise KeyError("Just a test.")
except DemoError as e:
print("It's a DemoError!")
print(str(e))
except KeyError as e:
print("Oh, It's a KeyError.")
再次执行程序,会看到KeyError被捕获:
I will raise a Exception in this process.
Oh, It's a KeyError.
在Python中,try-except不一定是要成对出现的。一个try块后可以有多个except块,就像上面看到的例子那样。
但是如果except捕获异常时有冲突怎么办?来看看下面的代码:
class DemoError(Exception):
def __init__(self, message):
self.message = "DemoError: " + message
def __str__(self):
return self.message
if __name__ == "__main__":
try:
print("I will raise a Exception in this process.")
raise KeyError("Just a test.")
except Exception as e:
print("I can not get your error type.")
except DemoError as e:
print("It's a DemoError!")
print(str(e))
except KeyError as e:
print("Oh, It's a KeyError.")
13行抛出了KeyError异常,而14行的 except Exception as e可以捕获任何异常类型。那么代码到底是执行14行的except代码块,还是执行19行针对KeyError捕获的except代码块呢?
看一下执行结果:
I will raise a Exception in this process.
I can not get your error type.
看来程序进入14行的代码块,并没有进入19行的except KeyError。
出现这种现象是因为except是顺序匹配异常的。如果匹配到可以捕获的异常,就进入该except块中。
还是刚刚的代码,稍稍调整下except块的顺序:
class DemoError(Exception):
def __init__(self, message):
self.message = "DemoError: " + message
def __str__(self):
return self.message
if __name__ == "__main__":
try:
print("I will raise a Exception in this process.")
raise KeyError("Just a test.")
except DemoError as e:
print("It's a DemoError!")
print(str(e))
except KeyError as e:
print("Oh, It's a KeyError.")
except Exception as e:
print("I can not get your error type.")
这时我们执行代码:
I will raise a Exception in this process.
Oh, It's a KeyError.
可以看到代码流程进入了except KeyError语句块中。
?在try-except的最后一个except块中可以加一个except Exception的语句块,用来捕获开发过程中意料之外的异常,不至于使程序崩溃。
2. finally语句
上面所说的try-except语句用于捕获异常。还有一种不太常见,且稍稍复杂的语句结构:try-except-finally。与之前的类似,异常在except中捕获,但是在except块执行完毕后,代码会接着在finally中继续执行。
class DemoError(Exception):
def __init__(self, message):
self.message = "DemoError: " + message
def __str__(self):
return self.message
if __name__ == "__main__":
try:
print("I will raise a Exception in this process.")
raise KeyError("Just a test.")
except DemoError as e:
print("It's a DemoError!")
print(str(e))
except KeyError as e:
print("Oh, It's a KeyError.")
except Exception as e:
print("I can not get your error type.")
finally:
print("In the finally...")
程序运行的结果如下:
I will raise a Exception in this process.
Oh, It's a KeyError.
In the finally...
?提示:finally语句是可选的,可以根据实际情况决定是否需要添加finally语句块
如果在except中使用return语句,那么还会执行finally语句块吗?
先来看看下面的代码:
def fun():
try:
print("I will raise a Exception in this process.")
raise KeyError("Just a test.")
except KeyError as e:
print("Oh, It's a KeyError.")
return
finally:
print("In the finally...")
if __name__ == "__main__":
fun()
注意在第8行,也就是finally语句之前,使用了return语句。程序的执行结果如下:
I will raise a Exception in this process.
Oh, It's a KeyError.
In the finally...
可以看出,尽管在except语句块中使用了return语句,但Python仍然会执行finally语句块。
Python在except中使用return会继续处理finally中的代码。
那么,,如果想强行在except中结束,而不处理finally中的代码呢?
恐怕只有删掉finally语句块了。(就算在except语句块中强行增加exit()也是不行的,Python依然会执行finally块)