Python3.5笔记——第9章 异常
Python3.5笔记
第9章 异常
什么是异常
一般情况下,在Python无法正常处理程序时就会发生异常。异常是Python的对象,表示一个错误。当Python脚本发生异常时,我们需要捕获并处理异常,否则程序会终止执行。如:
print(a)
输出:
Traceback (most recent call last):
File "D:/pyspace/hellopython/Chapter9.py", line 1, in <module>
print(a)
NameError: name 'a' is not defined
异常处理
处理异常,最简单的是使用try语句处理。try语句的基本格式为:try/except。try/except语句用来检测try语句块中的错误,从而让except语句捕获异常信息并处理。如果你不想在发生异常时结束程序,只需要在try语句块中捕获异常即可。语法格式如下:
try:
<语句> #运行别的代码
except<异常的名字>:
<语句> #如果在try部分发生了异常
示例如下:
#! /usr/bin/python3
# -*- coding:UTF-8 -*-
def test_exception(x,y):
try:
a = x/y
print('a=',a)
return a
except Exception:
print('程序发生异常,被除数为0')
test_exception(10,0)
输出:
程序发生异常,被除数为0
抛出异常
Python使用raise语句抛出一个指定的异常。使用类时程序会自动创建实例。
#raise Exception
#raise NameError('this is nameerror')
def test_exception2():
try:
raise NameError('This is a NameError')
except NameError:
print('发生异常!')
#raise 如果只想知道是否抛出了异常,不想处理。加上raise语句把异常再次抛出
test_exception2()
输出(捕获异常后不添加raise):
发生异常!
输出(捕获异常后添加raise):
Traceback (most recent call last):
发生异常!
File "D:/pyspace/hellopython/Chapter9.py", line 26, in <module>
test_exception2()
File "D:/pyspace/hellopython/Chapter9.py", line 21, in test_exception2
raise NameError('This is a NameError')
NameError: This is a NameError
在Python中,内建的异常类很多,比如:NameError、SyntaxError、TypeError、ValueError等,可以使用dir函数列出异常类的内容,并用在raise语句中。内建的异常类如表格:
异常名称 | 描述 |
---|---|
Exception | 常规错误的基类 |
AttributeError | 对象没有这个属性 |
IOError | 输入/输出操作失败 |
IndexError | 序列中没有此索引(index) |
KeyError | 映射中没有这个键 |
NameError | 未声明/初始化对象 |
SyntaxError | 语法错误 |
SystemError | 一般解释器系统错误 |
ValueError | 传入无效的参数 |
捕获多个异常
Python支持在一个try/excpet语句中处理多个异常,语法如下:
try:
<语句>
except <异常名1>:
<语句>
except <异常名2>:
<语句>
示例如下:
def test_expcetion3(a,b):
try:
c = a / b
#c = name
return c
except ZeroDivisionError:
print('除数不能为0')
except NameError:
print('对象未声明')
test_exception(10,0)
输出:
程序发生异常,被除数为0
使用一个块捕捉多个异常
如果需要使用一个块捕捉多个类型异常,可以将它们作为元组列出。使用该方式时,遇到的异常类型是元组中的任意一个,都会走异常流程。实例如下:
#! /usr/bin/python3
# -*- coding:UTF-8 -*-
def test_expceiton4(a,b):
try:
c = a/b
return c
except (ZeroDivisionError,NameError,TypeError):
print('发生了异常!')
test_expceiton4(10,0)
输出:
发生了异常!
如果我们希望多个except字句输出同样的信息,就没必要在几个except字句中重复输入字句,放到一个异常块中即可。
捕捉对象
如果需要输出错误时的栈堆信息,可以使用as e的形式,我们称之为捕捉对象。实例如下:
def test_expceiton5(a,b):
try:
c = a/b
return c
except (ZeroDivisionError,NameError,TypeError) as e:
print(e)
test_expceiton5(10,0)
输出:
division by zero
全捕捉
对于逃过了捕获指定异常的情况,我们根本无法预测会发生什么,也无法提前做任何准备。此时,与其使用不是捕获异常try/catch语句隐藏异常,不如让程序立刻崩溃。实例如下:
def test_expceiton5(a,b):
try:
c = a/b
return c
except:
print('全捕获异常')
test_expceiton5(10,'')
输出:
全捕获异常
异常中的else
try...except...else...语句,如果在try语句执行时没有发生异常,就会执行else语句后的语句(如果有else)。使用else语句,比把所有语句都放在try字句里更好,这样可以避免一些意想不到而except有没捕获的异常。格式如下:
try:
<语句>
except<异常名1>:
<语句>
except<异常名n>:
<语句>
else:
<语句> #如果没有发生异常
示例如下:
def test_expceiton6(a,b):
try:
c = a/b
except:
print('全捕获异常')
else:
print('没有发生异常')
test_expceiton6(10,5)
输出:
没有发生异常
自定义异常
尽管内建异常包含了大部分异常,可以满足很多要求,但有时还是要创建自己的异常类。可以通过创建一个新的exception类拥有自己的异常,异常应该继承自Exception类,可以直接继承,也可以间接继承。
因为错误就是类,因此捕获异常就是捕获该类的一个实例,我们自己编写的函数也可以抛出错误。如果要抛出错误,那么可以定义一个错误类,根据需要选择好继承关系,然后用raise语句抛出一个错误的实例。示例如下:
#! /usr/bin/python3
# -*- coding:UTF-8 -*-
# 自定义异常类最好以Error结尾,方便识别
class MyError(Exception):
def __init__(self):
pass
def __str__(self):
return 'this is my error'
def my_error_test():
try:
raise MyError()
except MyError as e:
print('exception e is:',e)
my_error_test()
输出:
exception e is: this is my error
finally子句
Python中finally子句需要和try语句一起使用,组成try/finally的语句形式,try/finally语句无论是否发生异常与否都将执行最后的代码。try、except、else、finally语句可以组合使用,执行顺序为:else在except之后,finally在except和else之后。示例如下:
def use_finally(a,b):
try:
c = a/b
print('c=',c)
except Exception:
print('发生异常')
else:
print('顺利上岸,没有发生异常')
finally:
print('不管是否发生异常,我都会出现!')
use_finally(10,0)#测试发生异常
#use_finally(10,5)#测试不发生异常
发生异常时,输出:
发生异常
不管是否发生异常,我都会出现!
不发生异常时,输出:
c= 2.0
顺利上岸,没有发生异常
不管是否发生异常,我都会出现!
异常和函数
如果异常在函数内引发而不被处理,就会传播至函数调用的地方。如果异常在函数调用的地方也没有被处理,就会继续传播,一直到达主程序。如果在主程序也没有被处理,异常就会被Python解释器捕获,输出一个错误信息,然后退出主程序。示例如下:
def divide_func(a,b):
return a/b
def exec_divide_func(a,b):
return divide_func(a,b)*10
def main(a,b):
return exec_divide_func(a,b)
main(10,0)
输出:
Traceback (most recent call last):
File "D:/pyspace/hellopython/Chapter9.py", line 131, in <module>
main(10,0)
File "D:/pyspace/hellopython/Chapter9.py", line 129, in main
return exec_divide_func(a,b)
File "D:/pyspace/hellopython/Chapter9.py", line 126, in exec_divide_func
return divide_func(a,b)*10
File "D:/pyspace/hellopython/Chapter9.py", line 123, in divide_func
return a/b
ZeroDivisionError: division by zero
由执行结果来看,divide_func函数中产生的异常通过divide_func和exec_divide_func函数传播,exec_divide_func函数中的异常,通过exec_divide_func和main函数传播,传播到函数调用处后由Python解释器处理,最终抛出栈堆异常。