异常处理方法及流程pytho(pytho异常处理有何作用)
导语:Python学习点滴04 - 学会异常处理(2)
前言我们在开发Python程序时经常会遇到一些错误(语法错误和异常),尤其是程序代码发生异常(Exceptions)时,如果不能及时捕获异常和有效处理异常,则程序运行会被终止,有可能会造成相应的后果;相反如果能及时捕获异常并有效处理异常,则能大大提高程序的健壮性。因此学会Python异常处理还是很有必要的。
本文会比较系统地介绍Python异常的相关知识。
本文分享内容的目录如下:
0. 前言
1. 了解Python错误
-1.1 语法错误
-1.2 异常
-1.3 Traceback信息
2. 了解内置异常类
-2.1 内置异常类层次结构
-2.2 异常基类
-2.3 具体异常类
-2.4 OS异常类
-2.5 警告异常类
3. 创建自定义异常类
4. 如何捕获和处理程序中的异常
-4.1 基本try-except语句块
-4.2 多重异常try-except语句块(基本情形1)
-4.3 多重异常try-except语句块(基本情形2)
-4.4 带嵌套的try-except语句块
-4.5 try-except-else语句块
-4.6 try-except-finally语句块
-4.7 try-except-else-finally语句块
5. 如何抛出一个异常
-5.1 如何抛出一个内置异常
-5.2 如何抛出一个自定义异常
6. 如何让程序输出更详细的异常信息
-6.1 面临问题-使用捕获的异常类只能获取较少的异常信息
-6.2 解决办法-使用traceback模块可以获取更详细的异常信息
7. 结束语
注:上篇文章《Python学习点滴04 - 学会异常处理(1)》中已经介绍了第0章-第3章的内容,本文继续介绍剩下的第4章-第7章内容。
本文开发环境为:
- Windows 7 64-bit
- Python 3.8.5 64-bit。
- IDLE (Python 3.8.5 64-bit)
1. Python错误本章主要介绍了Python语法错误、异常和Traceback信息。
详见上篇文章《Python学习点滴04 - 学会异常处理(1)》第1章内容介绍。
2. 了解内置异常类本章主要介绍了Python内置异常类的层次结构、异常基类、具体异常类、OS异常类和警告异常类。
详见上篇文章《Python学习点滴04 - 学会异常处理(1)》第2章内容介绍。
3. 自定义异常类本章主要介绍了如何自定义异常类。
详见上篇文章《Python学习点滴04 - 学会异常处理(1)》第3章内容介绍。
4. 如何捕获和处理程序中的异常Python语言是通过try-except语句块来实现捕获和处理程序异常的。该语句块存在几种组合方式,下面逐一进行介绍:
4.1 基本try-except语句块4.1.1 概述基本的try-except语句块只包含一个try子句块和一个except子句块(针对单个异常)。
– 每个try子句块包括:try:标志行和try子句(关键字try和except之间的1行或多行代码语句,即下面语法介绍或流程图中的“<可能会引发异常的语句块>”)。
– 每个except子句块包括:exceprt [<异常类型> [as <变量>]:语句和except子句(1行或多行,即下面语法介绍或流程图中的“<处理异常类型的语句块>”)。
捕获和处理异常的简单过程:
通常我们会把可能会引发异常的代码语句放置在try子句块的try子句中。当Python程序执行try子句中的代码语句时,如果程序抛出一个异常,则except语句会捕获到该异常。如果捕获的异常类型与except语句中指定的异常类型相匹配,则跳转到except子句块执行except子句。
其他要点:
1、如果except语句没有指定具体的异常类型(即except:形式的except语句),则可以捕获在try语句块中发生的所有类型的异常。
2、如果想except语句能捕获到在try语句块中发生的指定类型的异常,则可以采用except 异常类型:形式的except语句;
3、可以通过except 异常类型 as 变量:形式的except语句来将该指定类型的异常实例绑定到一个变量(通常用e表示)。如果except语句中指定的异常有类型变量,则该变量将作为未处理异常的消息的最后一部分(&39;)打印。
4.1.2 语法介绍 try: <可能会引起异常的语句块> except [<异常类型> [as <变量>]]: <处理异常语句块>
关键字except后两个方括号内容是可选项:
1、如果关键字except后直接就是冒号:,则表示没有指定具体的异常类型;
2、如果关键字except后带[<异常类型>]:,则表示指定了具体的异常类型;
3、如果关键字except后带[<异常类型> as <变量>]:,则表示指定了具体的异常类型,并给该类型的异常实例绑定了一个变量;
4.1.3 执行流程基本try-except语句块执行流程
执行流程说明如下:
Step1:首先执行try子句块中的try子句(即关键字try和except之间的1行或多行语句);
Step2:等待程序是否发生异常?
Step3-1:如果程序未发生异常,则跳过接下来的except子句块,完成本次try-except语句块的执行,进入“后续代码语句”的执行。
Step3-2:如果程序抛出了一个异常,则跳过try子句中剩下的部分,执行接下来的except子句块。
Step3-2-1:如果except语句捕获到的异常类型与except 异常类型 as 变量:语句中指定的异常类型匹配,则执行except子句块中的except子句(即流程图中的“处理异常类型语句块”),然后完成本次try-except语句块的执行。
Step3-2-2:如果异常的类型与except 关键字后面指定的异常类型不匹配,进而无法找到该类型异常的“处理异常类型的语句块”,则程序就立即终止,并在终端上输出相应的Traceback信息。
4.1.4 示例1 - 没有指定具体异常类型1、示例代码:(try_except1.py)
while True: print(&39;) try: num1 = eval(input(&39;)) num2 = eval(input(&39;)) result = num1/num2 print(&39; % (num1, num2, result)) except: print(&39;)
2、示例运行:
C:\exception> python try_except1.py ++++++开始计算两数相除的商:++++++ 请输入被除数:6 请输入除数:2 商 = 被除数/除数 = 6/2 = 3 ++++++开始计算两数相除的商:++++++ 请输入被除数:6 请输入除数:0 !!!!!!程序出错,请重新开始!!!!!! ++++++开始计算两数相除的商:++++++ 请输入被除数:
从上述示例运行的记录可以看到,当分别输入数字6和2后,程序计算并输出正确的商值3;当分别输入数字6和0后,因为检测到数字0作为除数了,程序抛出一个异常(异常类型为ZeroDivisionError),被except:语句捕获到了,直接跳转到except语句块(代码第9行)执行:打印输出提示信息。然后程序并不会终止,会继续开始一个新的循环。
4.1.5 示例2 - 指定了具体异常类型1、示例代码:(try_except2.py)
while True: print(&39;) try: num1 = eval(input(&39;)) num2 = eval(input(&39;)) result = num1/num2 print(&39; % (num1, num2, result)) except ZeroDivisionError: print(&39; % (ZeroDivisionError))
注意上述代码第8行的变化(增加了指定的异常类型)。
2、示例运行:
C:\exception> python try_except2.py ++++++开始计算两数相除的商:++++++ 请输入被除数:6 请输入除数:0 程序出现异常:<class &39;>,请重新开始! ++++++开始计算两数相除的商:++++++ 请输入被除数:
从上述示例运行的记录可以看到,当分别输入数字6和0后,因为检测到数字0作为除数了,,程序发出一个异常(异常类型为ZeroDivisionError),被except ZeroDivisionError:语句捕获到了,直接跳转到except语句块(代码第9行)执行:打印输出异常信息。然后程序并不会终止,会继续开始一个新的循环。
4.1.6 示例3 - 指定了具体异常类型,且绑定了变量1、示例代码:(try_except3.py)
while True: print(&39;) try: num1 = eval(input(&39;)) num2 = eval(input(&39;)) result = num1/num2 print(&39; % (num1, num2, result)) except ZeroDivisionError as e: print(&39; % (e))
注意上述代码第8行的变化(增加了别名定义)。
2、示例运行:
C:\exception> python try_except3.py ++++++开始计算两数相除的商:++++++ 请输入被除数:6 请输入除数:0 程序出现异常:division by zero,请重新开始! ++++++开始计算两数相除的商:++++++ 请输入被除数:
从上述示例运行的记录可以看到,当分别输入数字6和0后,因为检测到数字0作为除数了,程序抛出一个异常(异常类型为ZeroDivisionError),被except ZeroDivisionError as e:语句捕获到了,直接跳转到except语句块(代码第9行)执行:打印输出异常信息。然后程序并不会终止,会继续开始一个新的循环。
4.2 多重异常try-except语句块(基本情形1)4.2.1 概述在本文第4.1章节“基本try-except语句块”中介绍的是捕获和处理一种异常的情况,在实际开发过程中还存在有时需要分别捕获不同类型的异常(也称多重异常)并加以相应处理的情形(有两种基本情形)。
基本情形1:如果在try语句块中一处可能引发异常的代码语句会引发多种类型的异常,或者在try语句块中有多条代码语句可能引发不同类型的异常,而且针对每种类型的异常都需要有不同的处理语句,那么就可以采用“多重异常try-except语句块(基本情形1)”来实现,即在try语句块后跟多个except语句块,根据每个except 异常类型 as 变量:语句捕获到程序抛出的不同类型的异常,选择匹配的except语句块执行except子句(“处理特定异常类型的语句块”)。
注:程序会依从上往下的顺序,依次执行每个带指定异常类型的except语句块,不带指定异常类型的except语句块放置在最后(以捕获和处理之前没有匹配到的异常)。即在try语句块后可以有多个except语句块,但只能是最后一个不带指定异常类型的except语句块,之前的都应该是带指定异常类型的except语句块。
4.2.2 语法介绍 ...... try: <可能会引起异常的语句块> except <异常类型1> [as <变量>]: <处理异常类型1的语句块> except <异常类型2> [as <变量>]: <处理异常类型2的语句块> ...... except: <处理其他异常类型的语句块> ......
4.2.3 执行流程多重异常try-except语句块(基本清晰1)执行流程
4.2.4 示例1、示例代码:(try_mul_except.py)
while True: print(&39;) try: num1 = eval(input(&39;)) num2 = eval(input(&39;)) result = num1/num2 print(&39; % (num1, num2, result)) except ZeroDivisionError as e: print(&39; % (e)) except NameError as e: print(&39; % (e)) except: print(&39;)
2、示例运行:
C:\exception> python try_mul_except.py ++++++开始计算两数相除的商:++++++ 请输入被除数:6 请输入除数:0 程序出现ZeroDivisionError异常:division by zero,请重新开始! ++++++开始计算两数相除的商:++++++ 请输入被除数:a 程序出现NameError异常:name &39; is not defined,请重新开始! ++++++开始计算两数相除的商:++++++ 请输入被除数:
从上述示例运行的记录可以看到:
1、当分别输入数字6和0后,因为检测到数字0作为除数了,程序发出一个异常(异常类型为ZeroDivisionError),被except ZeroDivisionError as e:语句捕获到了,直接跳转到except ZeroDivisionError as e:语句块(代码第9行)执行:打印输出提示信息。然后程序并不会终止,会继续开始一个新的循环;
2、当输入字母a后,因为检测到字母a未被定义,程序发出一个异常(异常类型为NameError),被except NameError as e:语句捕获到了,直接跳转到except NameError as e:语句块(代码第11行)执行:打印输出提示信息。然后程序并不会终止,会继续开始一个新的循环。
4.3 多重异常try-except语句块(基本情形2)4.3.1 概述在本文第4.1章节“基本try-except语句块”中介绍的是捕获和处理一种异常的情况,在实际开发过程中还存在有时需要分别捕获不同类型的异常(也称多重异常)并加以相应处理的情形(有两种基本情形)。
基本情形2:如果在try语句块中一处可能引发异常的代码语句会引发多种类型的异常,或者在try语句块中有多条代码语句可能引发不同类型的异常,而且针对每种类型的异常都是相同的处理语句,那么就可以采用“多重异常try-except语句块(基本情形2)”来实现,即在except语句关键词except后将多个异常类型命名为带括号的元组。这样只要捕获到程序抛出的异常类型跟元组中匹配的异常类型时,就跳转执行except语句块中的except子句(“统一处理异常类型的语句块”)。
4.3.2 语法介绍 ...... try: <可能会引起异常的语句块> except (异常类型1, 异常类型2, ..., 异常类型n) as <变量>: <统一处理各类型异常的语句块> ......
4.3.3 执行流程多重异常try-except语句块(基本情形2)执行流程
4.3.4 示例1、示例代码:(try_exceptmul.py)
while True: print(&39;) try: num1 = eval(input(&39;)) num2 = eval(input(&39;)) result = num1/num2 print(&39; % (num1, num2, result)) except (ZeroDivisionError, NameError) as e: print(&39; % (e))
2、示例运行:
C:\exception> python try_exceptmul.py ++++++开始计算两数相除的商:++++++ 请输入被除数:6 请输入除数:0 程序出现异常:division by zero,请重新开始! ++++++开始计算两数相除的商:++++++ 请输入被除数:a 程序出现异常:name &39; is not defined,请重新开始! ++++++开始计算两数相除的商:++++++ 请输入被除数:
从上述示例运行的记录可以看到:
1、当分别输入数字6和0后,因为检测到数字0作为除数了,程序发出一个异常(异常类型为ZeroDivisionError),被except (ZeroDivisionError, NameError) as e:语句捕获到了,直接跳转到except语句块(代码第9行)执行:打印输出提示信息。然后程序并不会终止,会继续开始一个新的循环;
2、当输入字母a后,因为检测到字母a未被定义,程序发出一个异常(异常类型为NameError),被except (ZeroDivisionError, NameError) as e:语句捕获到了,直接跳转到except语句块(代码第9行)执行:打印输出提示信息。然后程序并不会终止,会继续开始一个新的循环。
4.4 带嵌套的try-except语句块4.4.1 概述Python支持带嵌套的try-except语句块。
注意:带嵌套的try-except语句块会使程序流程变得复杂,所以建议尽量不使用带嵌套的try-except语句块。
4.4.2 语法介绍 ...... try: <可能会引起异常的语句块1> try: <可能会引起异常的语句块2> except [<异常类型> [as <变量>]]: <处理异常类型的语句块2> except [<异常类型> [as <变量>]]: <处理异常类型的语句块1> ......
4.4.3 执行流程带嵌套的try-except语句块执行流程
4.4.4 示例1、示例代码:(try_except_nest.py)
while True: print(&39;) try: num1 = eval(input(&39;)) num2 = eval(input(&39;)) try: result = num1/num2 print(&39; % (num1, num2, result)) except ZeroDivisionError as e: print(&39; % (e)) except NameError as e: print(&39; % (e))
2、示例运行:
C:\exception> python try_except_nest.py ++++++开始计算两数相除的商:++++++ 请输入被除数:6 请输入除数:0 程序出现异常:division by zero,请重新开始! ++++++开始计算两数相除的商:++++++ 请输入被除数:a 程序出现异常:name &39; is not defined,请重新开始! ++++++开始计算两数相除的商:++++++ 请输入被除数:
从上述示例运行的记录可以看到:
1、当分别输入数字6和0后,因为检测到数字0作为除数了,程序发出一个异常(异常类型为ZeroDivisionError),被except ZeroDivisionError as e:语句捕获到了,直接跳转到except语句块(代码第10行)执行:打印输出提示信息。然后程序并不会终止,会继续开始一个新的循环;
2、当输入字母a后,因为检测到字母a未被定义,程序发出一个异常(异常类型为NameError),被except NameError as e:语句捕获到了,直接跳转到except子句块(代码第12行)执行:打印输出提示信息。然后程序并不会终止,会继续开始一个新的循环。
4.5 try-except-else语句块4.5.1 概述try-except语句块有一个可选的else子句块,组合构建成try-except-else语句块。需要注意:else子句块必须放置在所有的except子句块之后。对于在try子句块不引发异常时必须执行的代码来说很有用。
try-except-else语句块的简单过程:
通常我们会把可能会引发异常的代码语句放置在try子句块的try子句中。当Python程序执行try子句中的代码语句时,如果程序抛出一个异常,则except语句会捕获到该异常,然后跳转到except语句块执行except子句(即处理异常的语句块);如果Python程序未发生异常,则会跳转到else子句块执行正常代码语句块。
4.5.2 语法介绍 ...... try: <可能会引起异常的语句块> except [<异常类型> [as <变量>]]: <处理异常语句块> else: <正常代码语句块> ......
4.5.3 执行流程try-except-else语句块执行流程
4.5.4 示例1、示例代码:(try_except_else.py)
while True: print(&39;) try: num1 = eval(input(&39;)) num2 = eval(input(&39;)) result = num1/num2 except (ZeroDivisionError, NameError) as e: print(&39; % (e)) else: print(&39; % (num1, num2, result))
2、示例运行:
C:\exception> python try_except_else.py ++++++开始计算两数相除的商:++++++ 请输入被除数:6 请输入除数:2 商 = 被除数/除数 = 6/2 = 3 ++++++开始计算两数相除的商:++++++ 请输入被除数:6 请输入除数:0 程序出现异常:division by zero,请重新开始! ++++++开始计算两数相除的商:++++++ 请输入被除数:
从上述示例运行的记录可以看到:
1、当分别输入数字6和2后,程序接着执行result = num1/num2语句,此时程序未发生异常,故直接跳转到else子句块(代码第10行)执行,输出正确的商值3;
2、当分别输入数字6和0后,程序接着执行result = num1/num2语句,此时程序检测到数字0作为除数了,程序便发出一个异常(异常类型为ZeroDivisionError),被except (ZeroDivisionError, NameError) as e:语句捕获到了,直接跳转到except子句块(代码第8行)执行:打印输出提示信息。然后程序并不会终止,会继续开始一个新的循环。
4.6 try-except-finally语句块4.6.1 概述try-except语句块有一个可选的finally子句块,组合构建成try-except-finally语句块。finally子句块用于定义必须在所有情况下执行的清理操作。
在实际应用try-except语句块的程序中,如果在try子句块中存在占用资源的情形(如:已经打开的文件、已经连接的数据库、已经连接的网络等都会占用计算机资源),则在正常执行完try子句块中所有代码语句后,或者程序抛出的异常被except语句捕获并执行except子句后,都应该做好清理工作(即释放这些资源)。有了finally子句块后,针对无论程序是否发生异常,在执行try-except-finally语句块后都能执行清理工作(即释放这些资源,如:关闭打开的文件、关闭连接的数据库、关闭连接的网络等)。
try-except-finally语句块的简单过程:
通常我们会把可能会引发异常的代码语句放置在try子句块的try子句中。当Python程序执行try子句中的代码语句时,如果程序抛出一个异常,则except语句会捕获到该异常,然后跳转到except子句块执行except子句(即处理异常的语句块),最后会跳转到finally子句块执行清理操作的语句块;如果Python程序未发生异常,则在执行完try子句后会直接跳转到finally子句块执行清理操作的语句块。
try-except-finally语句块的注意要点:
1、finally子句块必须放置在所有的except子句块之后,finally子句块是作为try-except-finally语句块结束前的最后一项任务被执行。
2、在执行try子句块过程中,不论程序是否发生异常,finally子句块都会被执行。
3、如果程序在执行 try子句块过程中发生了异常,该异常将由某个except子句块进行处理。 如果该异常没有被某个except子句块所处理,则该异常会在finally子句块执行之后被重新引发。
4、异常也可能在执行 except子句块 或 else子句块 过程中发生。 同样地,该异常会在finally子句块执行之后被重新引发。
5、如果程序在执行 try子句块过程中遇到一个 break, continue 或 return 语句,则 finally子句块将在执行 break, continue 或 return 语句之前被执行。
6、如果finally子句块中包含一个 return 语句,则返回值将来自finally子句块的某个 return 语句的返回值,而非来自try子句块的 return 语句的返回值。
4.6.2 语法介绍 ...... try: <可能会引起异常的语句块> except [<异常类型> [as <变量>]]: <处理异常的语句块> finally: <清理工作的语句块> ......
4.6.3 执行流程try-except-finally语句块执行流程
4.6.4 示例1、示例代码:(try_except_finally.py)
while True: print(&39;) try: num1 = eval(input(&39;)) num2 = eval(input(&39;)) result = num1/num2 print(&39; % (num1, num2, result)) except (ZeroDivisionError, NameError) as e: print(&39; % (e)) finally: print(&39;)
2、示例运行:
C:\exception> python try_except_finally.py ++++++开始计算两数相除的商:++++++ 请输入被除数:6 请输入除数:2 商 = 被除数/除数 = 6/2 = 3 ------本次计算结束!------ ++++++开始计算两数相除的商:++++++ 请输入被除数:6 请输入除数:0 程序出现异常:division by zero,请重新开始! ------本次计算结束!------ ++++++开始计算两数相除的商:++++++ 请输入被除数:a 程序出现异常:name &39; is not defined,请重新开始! ------本次计算结束!------ ++++++开始计算两数相除的商:++++++ 请输入被除数:
从上述示例运行的记录可以看到:
1、当分别输入数字6和2后,程序接着执行result = num1/num2语句和print()语句输出正确的商值3,此时程序未发生异常,故直接跳转到finally子句块(代码第11行)执行,输出提示信息(------本次计算结束!------);
2、当分别输入数字6和0后,程序接着执行result = num1/num2语句,此时程序检测到数字0作为除数了,程序便发出一个异常(异常类型为ZeroDivisionError),被except (ZeroDivisionError, NameError) as e:语句捕获到了,直接跳转到except子句块(代码第9行)执行:打印输出异常提示信息。此时程序并不会终止,接着跳转到finally子句块(代码第11行)执行,输出提示信息(------本次计算结束!------);然后程序会继续开始一个新的循环。
3、当输入字母a后,因为检测到字母a未被定义,程序发出一个异常(异常类型为NameError),被except (ZeroDivisionError, NameError) as e:语句捕获到了,直接跳转到except (ZeroDivisionError, NameError) as e:子句块(代码第9行)执行:打印输出异常提示信息。此时程序并不会终止,接着跳转到finally子句块(代码第11行)执行,输出提示信息(------本次计算结束!------);然后程序会继续开始一个新的循环。
4.7 try-except-else-finally语句块4.7.1 概述try-except语句块可选的else子句块和finally子句块可以一起使用,组合构建成try-except-else-finally语句块。
try-except-else-finally语句块的简单过程:
通常我们会把可能会引发异常的代码语句放置在try子句块的try子句中。当Python程序执行try子句中的代码语句时:
1、如果程序抛出一个异常,则except语句会捕获到该异常,然后跳转到except语句块执行except子句(即处理异常的语句块),完成后再跳转到finally子句块执行清理操作的语句块;
2、如果Python程序未发生异常,则在执行完try子句后会直接跳转到else子句块执行else子句,完成后再跳转到finally子句块执行清理操作的语句块。
4.7.2 语法介绍 ...... try: <可能会引起异常的语句块> except [<异常类型> [as <变量>]]: <处理异常的语句块> else: <正常代码的语句块> finally: <清理工作的语句块> ......
4.7.3 执行流程try-except-else-finally语句块执行流程
4.7.4 示例1、示例代码:(try_except_else_finally.py)
while True: print(&39;) try: num1 = eval(input(&39;)) num2 = eval(input(&39;)) result = num1/num2 except (ZeroDivisionError, NameError) as e: print(&39; % (e)) else: print(&39; % (num1, num2, result)) finally: print(&39;)
2、示例运行:
C:\exception> python try_except_else_finally.py ++++++开始计算两数相除的商:++++++ 请输入被除数:6 请输入除数:2 商 = 被除数/除数 = 6/2 = 3 ------本次计算结束!------ ++++++开始计算两数相除的商:++++++ 请输入被除数:6 请输入除数:0 程序出现异常:division by zero,请重新开始! ------本次计算结束!------ ++++++开始计算两数相除的商:++++++ 请输入被除数:a 程序出现异常:name &39; is not defined,请重新开始! ------本次计算结束!------ ++++++开始计算两数相除的商:++++++ 请输入被除数:
从上述示例运行的记录可以看到:
1、当分别输入数字6和2后,程序接着执行result = num1/num2语句和print()语句输出正确的商值3,此时程序未发生异常,故直接跳转到else子句块(代码第10行)执行,输出提示信息(商 = 被除数/除数 = 6/2 = 3),最后跳转到finally子句块(代码第12行)执行,输出提示信息(------本次计算结束!------);
2、当分别输入数字6和0后,程序接着执行result = num1/num2语句,此时程序检测到数字0作为除数了,程序便发出一个异常(异常类型为ZeroDivisionError),被except (ZeroDivisionError, NameError) as e:语句捕获到了,直接跳转到except子句块(代码第8行)执行:打印输出异常提示信息。此时程序并不会终止,接着跳转到finally子句块(代码第12行)执行,输出提示信息(------本次计算结束!------);然后程序会继续开始一个新的循环。
3、当输入字母a后,因为检测到字母a未被定义,程序发出一个异常(异常类型为NameError),被except (ZeroDivisionError, NameError) as e:语句捕获到了,直接跳转到except (ZeroDivisionError, NameError) as e:子句块(代码第8行)执行:打印输出异常提示信息。此时程序并不会终止,接着跳转到finally子句块(代码第12行)执行,输出提示信息(------本次计算结束!------);然后程序会继续开始一个新的循环。
5. 如何抛出一个异常开发者可以使用raise语句来抛出一个异常(即强制发生一个指定的异常)。
raise关键字后面跟的参数就是要抛出的异常。该参数必须是一个异常实例或者是一个异常类(派生自 Exception 类)。如果传递的是一个异常类,它将通过调用没有参数的构造函数来隐式实例化。如raise NameError实质上是raise NameError()的简写。
抛出的异常实例还可以带参数,表示对该异常的解释。
5.1 抛出一个内置异常一、抛出一个ValueError异常类示例
>>> raise ValueError Traceback (most recent call last): File &1><pyshell34;, line 1, in <module> raise NameError() NameError
三、抛出一个ZeroDivisionError异常实例(不带参数)示例
>>> raise ZeroDivisionError(&39;) Traceback (most recent call last): File &3>&39;除数不能为零!&39;这是NameError异常示例&39;一个异常出现了!<pyshell34;, line 2, in <module> raise NameError(&39;) NameError: 这是NameError异常示例
5.2 抛出一个自定义异常开发者如果想抛出一个自定义异常,则首先必须先完成自定义异常类;然后再使用raise语句,象抛出一个内置异常一样抛出这个自定义异常。
下面示例如何抛出一个已经自定义好的自定义异常类MyException:
39;++++++开始计算两数相除的商:++++++&39;请输入被除数:&39;请输入除数:& 手动抛出一个自定义异常 raise MyException(&39;) result = num1/num2 print(&39; % (num1, num2, result)) except MyException as e: print(&39; % (e)) except NameError as e: print(&39; % (e)) except: print(&39;)
运行结果:
C:\exception> python try_except_custom.py ++++++开始计算两数相除的商:++++++ 请输入被除数:6 请输入除数:0 程序出现MyException异常:&39;,请重新开始! ++++++开始计算两数相除的商:++++++ 请输入被除数:a 程序出现NameError异常:name &39; is not defined,请重新开始! ++++++开始计算两数相除的商:++++++ 请输入被除数:
1、当分别输入数字6和0后,程序会接着执行到raise MyException(&39;)语句,此时程序代码语句手动抛出一个异常(是自定义异常MyException),被except MyException as e:语句捕获到了,直接跳转到except MyException as e:子句块(代码第19行)执行:打印输出异常提示信息(程序出现MyException异常:&39;,请重新开始!)。此时程序并不会终止,会继续开始一个新的循环。
3、当输入字母a后,因为检测到字母a未被定义,程序发出一个异常(异常类型为NameError),被except NameError as e:语句捕获到了,直接跳转到except NameError as e:子句块(代码第21行)执行:打印输出提示信息(程序出现NameError异常:name &39; is not defined,请重新开始!)。然后程序并不会终止,会继续开始一个新的循环。
6. 如何让程序输出更详细的异常信息通过上述章节的介绍,我们已经可以熟练掌握在Python代码中使用多种组合try-except语句,来有效提高程序的健壮性,减少因出现异常而终止程序运行的情况发生。
6.1 面临的问题 - 使用捕获的异常类只能获取较少的异常信息一、问题描述:
我们现在已经可以在Python程序运行过程中出现异常时通过捕获的异常类输出相应的异常信息了。虽然可以通过一些技术手段来辅助更好显示异常信息,但这些异常信息相比Traceback信息要简单很多,不利于开发者定位出现异常的具体位置。
二、示例代码:
while True: print(&39;) try: num1 = eval(input(&39;)) num2 = eval(input(&39;)) result = num1/num2 print(&39; % (num1, num2, result)) except ZeroDivisionError as e: print(&39; % (e)) except NameError as e: print(&39; % (e)) except: print(&39;)
三、示例运行:
C:\exception> python try_except.py ++++++开始计算两数相除的商:++++++ 请输入被除数:6 请输入除数:0 程序出现ZeroDivisionError异常:division by zero,请重新开始! ++++++开始计算两数相除的商:++++++ 请输入被除数:a 程序出现NameError异常:name &39; is not defined,请重新开始! ++++++开始计算两数相除的商:++++++ 请输入被除数:
可以看到示例程序运行过程中,当出现第2个输入数为0时,虽不会终止程序运行,同时也会在终端输出异常信息:程序出现ZeroDivisionError异常:division by zero,请重新开始!,但该异常信息对开发者而言还是有点简单,希望能定位到具体位置(哪个文件、哪行代码、哪条语句)。
6.2 解决办法 - 使用traceback模块可以获取更详细的异常信息针对第6.1章节所描述的问题,开发者可以使用Python内置库(traceback模块)的print_exc()函数来实现获取更详细的异常信息。也可以结合使用Python内置库:sys模块的exc_info()函数和traceback模块的print_exc()函数来实现获取更详细的异常信息。
6.2.1 解决办法1 - 直接使用traceback模块的print_exc()函数实现一、解决办法描述:
Python内置库traceback模块提供了一个标准接口print_exc()函数来提取、格式化和打印 Python 程序的堆栈跟踪结果。它完全模仿Python 解释器在打印堆栈跟踪结果时的行为。
二、示例代码:(try_except_traceback.py)
import traceback while True: print(&39;) try: num1 = eval(input(&39;)) num2 = eval(input(&39;)) result = num1/num2 print(&39; % (num1, num2, result)) except: print(&39;) traceback.print_exc() print(&39;)
三、示例程序运行结果:
C:\exception> python try_except_traceback.py ++++++开始计算两数相除的商:++++++ 请输入被除数:6 请输入除数:0 ---程序发生如下异常:--- Traceback (most recent call last): File , line 8, in <module> result = num1/num2 ZeroDivisionError: division by zero ---请重新开始!--- ++++++开始计算两数相除的商:++++++ 请输入被除数:a ---程序发生如下异常:--- Traceback (most recent call last): File , line 6, in <module> num1 = eval(input(&39;)) File , line 1, in <module> NameError: name &39; is not defined ---请重新开始!--- ++++++开始计算两数相除的商:++++++ 请输入被除数:
可以看到示例程序运行过程中,当出现输入数为字母a或第2个输入数为数字0时,程序发生异常,但并不会终止程序运行,同时也会在终端输出了的异常信息(Traceback信息):除了最后一行直接输出了异常类别和异常信息外,还在直接定位了出现异常的具体位置(文件名、代码行、出错语句)。
四、补充信息:
1、traceback.print_exc()直接打印出异常信息;
traceback.print_exc()中可以接收file参数,直接将异常信息写入到指定文件(如当前目录下的errorlog.txt文件)中。
traceback.print_exec(file=open(&39;, &39;))
2、traceback.format_exc()将异常信息返回一个字符串;
3、traceback.print(traceback.format_exc())的效果跟traceback.print_exc()一样,都可以直接打印出异常信息。
6.2.2 解决办法2 - 结合使用sys模块的exc_info()函数和traceback模块的print_exc()函数实现一、解决办法描述:
Python程序的Traceback信息均来源于traceback object,而这个traceback object通常是通过函数sys.exc_info()来获取的。sys.exc_info()获取当前处理的异常的相关信息,并返回一个元组。元组的第一个元素:exc_type是异常的类型;第二个元素:exc_value是异常的值;第三个元素:exc_tb就是上面说到的traceback object,traceback object中包含出错的行数、位置等数据。有了traceback object就可以通过Python内置库(traceback模块)来打印和格式化traceback的相关信息了。
下面示例通过sys.exc_info()函数来获取当前处理的异常的相关信息,然后通过traceback.print_exception()函数对这些异常数据进行整理输出。
二、示例代码:(try_except_systraceback.py)
import sys, traceback while True: print(&39;) try: num1 = eval(input(&39;)) num2 = eval(input(&39;)) result = num1/num2 print(&39; % (num1, num2, result)) except: print(&39;) exc_type, exc_value, exc_tb = sys.exc_info() traceback.print_exception(exc_type, exc_value, exc_tb) print(&39;)
三、示例运行结果:
C:\exception> python try_except_systraceback.py ++++++开始计算两数相除的商:++++++ 请输入被除数:6 请输入除数:0 ---程序发生如下异常:--- Traceback (most recent call last): File , line 8, in <module> result = num1/num2 ZeroDivisionError: division by zero ---请重新开始!--- ++++++开始计算两数相除的商:++++++ 请输入被除数:a ---程序发生如下异常:--- Traceback (most recent call last): File , line 6, in <module> num1 = eval(input(&39;)) File , line 1, in <module> NameError: name &39; is not defined ---请重新开始!--- ++++++开始计算两数相除的商:++++++ 请输入被除数:
可以看到示例程序运行过程中,当出现输入数为字母a或第2个输入数为数字0时,程序发生异常,但并不会终止程序运行,同时也会在终端输出了的异常信息(Traceback信息):除了最后一行直接输出了异常类别和异常信息外,还在直接定位了出现异常的具体位置(文件名、代码行、出错语句)。
结束语相信经过本文的介绍,大家应该对Python编程过程中的错误(语法错误、异常和Traceback信息)、Python内置异常类(内置异常类层次结构、异常基类、具体异常类、OS异常类、警告异常类)、自定义异常类、如何捕获和处理异常(基本try-except语句块、两种情形的多重异常try-except语句块、带嵌套的try-except语句块、try-except-else语句块、try-except-finally语句块和try-except-else-finally语句块)、如何抛出一个异常(内置异常、自定义异常)、如何让程序输出更详细的异常信息(面临问题、解决办法)有了比较深入的了解,应该能够在Python编程过程中利用好异常处理(多种组合try-except语句块)来使Python程序更加健壮了。
希望本文能对您有所帮助! 喜欢的话就点个赞加关注支持一下哈:)
本文内容由小春整理编辑!