前言

我们在开发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子句(关键字tryexcept之间的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语句中指定的异常有类型变量,则该变量将作为未处理异常的消息的最后一部分('详细信息')打印。

4.1.2 语法介绍

 try:      except [ [as ]]:     

关键字except后两个方括号内容是可选项:

1、如果关键字except后直接就是冒号:,则表示没有指定具体的异常类型;

2、如果关键字except后带[]:,则表示指定了具体的异常类型;

3、如果关键字except后带[ as ]:,则表示指定了具体的异常类型,并给该类型的异常实例绑定了一个变量;

4.1.3 执行流程

基本try-except语句块执行流程

执行流程说明如下:

Step1:首先执行try子句块中的try子句(即关键字tryexcept之间的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('++++++开始计算两数相除的商:++++++')     try:          num1 = eval(input('请输入被除数:'))         num2 = eval(input('请输入除数:'))         result = num1/num2         print('商 = 被除数/除数 = %d/%d = %d' % (num1, num2, result))     except:         print('!!!!!!程序出错,请重新开始!!!!!!')

2、示例运行:

 C:exception> python try_except1.py ++++++开始计算两数相除的商:++++++ 请输入被除数:6 请输入除数:2 商 = 被除数/除数 = 6/2 = 3 ++++++开始计算两数相除的商:++++++ 请输入被除数:6 请输入除数:0 !!!!!!程序出错,请重新开始!!!!!! ++++++开始计算两数相除的商:++++++ 请输入被除数:

从上述示例运行的记录可以看到,当分别输入数字62后,程序计算并输出正确的商值3;当分别输入数字60后,因为检测到数字0作为除数了,程序抛出一个异常(异常类型为ZeroDivisionError),被except:语句捕获到了,直接跳转到except语句块(代码第9行)执行:打印输出提示信息。然后程序并不会终止,会继续开始一个新的循环。

4.1.5 示例2 - 指定了具体异常类型

1、示例代码:(try_except2.py)

 while True:     print('++++++开始计算两数相除的商:++++++')     try:          num1 = eval(input('请输入被除数:'))         num2 = eval(input('请输入除数:'))         result = num1/num2         print('商 = 被除数/除数 = %d/%d = %d' % (num1, num2, result))     except ZeroDivisionError:         print('程序出现异常:%s,请重新开始!' % (ZeroDivisionError))

注意上述代码第8行的变化(增加了指定的异常类型)。

2、示例运行:

 C:exception> python try_except2.py ++++++开始计算两数相除的商:++++++ 请输入被除数:6 请输入除数:0 程序出现异常:,请重新开始! ++++++开始计算两数相除的商:++++++ 请输入被除数:

从上述示例运行的记录可以看到,当分别输入数字60后,因为检测到数字0作为除数了,,程序发出一个异常(异常类型为ZeroDivisionError),被except ZeroDivisionError:语句捕获到了,直接跳转到except语句块(代码第9行)执行:打印输出异常信息。然后程序并不会终止,会继续开始一个新的循环。

4.1.6 示例3 - 指定了具体异常类型,且绑定了变量

1、示例代码:(try_except3.py)

 while True:     print('++++++开始计算两数相除的商:++++++')     try:          num1 = eval(input('请输入被除数:'))         num2 = eval(input('请输入除数:'))         result = num1/num2         print('商 = 被除数/除数 = %d/%d = %d' % (num1, num2, result))     except ZeroDivisionError as e:         print('程序出现异常:%s,请重新开始!' % (e))

注意上述代码第8行的变化(增加了别名定义)。

2、示例运行:

 C:exception> python try_except3.py ++++++开始计算两数相除的商:++++++ 请输入被除数:6 请输入除数:0 程序出现异常:division by zero,请重新开始! ++++++开始计算两数相除的商:++++++ 请输入被除数:

从上述示例运行的记录可以看到,当分别输入数字60后,因为检测到数字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  [as ]:      except  [as ]:          ...... except:      ......

4.2.3 执行流程

多重异常try-except语句块(基本清晰1)执行流程

4.2.4 示例

1、示例代码:(try_mul_except.py)

 while True:     print('++++++开始计算两数相除的商:++++++')     try:          num1 = eval(input('请输入被除数:'))         num2 = eval(input('请输入除数:'))         result = num1/num2         print('商 = 被除数/除数 = %d/%d = %d' % (num1, num2, result))     except ZeroDivisionError as e:         print('程序出现ZeroDivisionError异常:%s,请重新开始!' % (e))     except NameError as e:         print('程序出现NameError异常:%s,请重新开始!' % (e))     except:         print('程序出现其他异常,请重新开始!')

2、示例运行:

 C:exception> python try_mul_except.py ++++++开始计算两数相除的商:++++++ 请输入被除数:6 请输入除数:0 程序出现ZeroDivisionError异常:division by zero,请重新开始! ++++++开始计算两数相除的商:++++++ 请输入被除数:a 程序出现NameError异常:name 'a' is not defined,请重新开始! ++++++开始计算两数相除的商:++++++ 请输入被除数:

从上述示例运行的记录可以看到:

1、当分别输入数字60后,因为检测到数字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('++++++开始计算两数相除的商:++++++')     try:          num1 = eval(input('请输入被除数:'))         num2 = eval(input('请输入除数:'))         result = num1/num2         print('商 = 被除数/除数 = %d/%d = %d' % (num1, num2, result))     except (ZeroDivisionError, NameError) as e:         print('程序出现异常:%s,请重新开始!' % (e))

2、示例运行:

  C:exception> python try_exceptmul.py ++++++开始计算两数相除的商:++++++ 请输入被除数:6 请输入除数:0 程序出现异常:division by zero,请重新开始! ++++++开始计算两数相除的商:++++++ 请输入被除数:a 程序出现异常:name 'a' is not defined,请重新开始! ++++++开始计算两数相除的商:++++++ 请输入被除数:

从上述示例运行的记录可以看到:

1、当分别输入数字60后,因为检测到数字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:            try:                except [ [as ]]:            except [ [as ]]:        ......

4.4.3 执行流程

带嵌套的try-except语句块执行流程

4.4.4 示例

1、示例代码:(try_except_nest.py)

 while True:     print('++++++开始计算两数相除的商:++++++')     try:          num1 = eval(input('请输入被除数:'))         num2 = eval(input('请输入除数:'))         try:             result = num1/num2             print('商 = 被除数/除数 = %d/%d = %d' % (num1, num2, result))         except ZeroDivisionError as e:             print('程序出现ZeroDivisionError异常:%s,请重新开始!' % (e))     except NameError as e:         print('程序出现NameError异常:%s,请重新开始!' % (e))

2、示例运行:

  C:exception> python try_except_nest.py ++++++开始计算两数相除的商:++++++ 请输入被除数:6 请输入除数:0 程序出现异常:division by zero,请重新开始! ++++++开始计算两数相除的商:++++++ 请输入被除数:a 程序出现异常:name 'a' is not defined,请重新开始! ++++++开始计算两数相除的商:++++++ 请输入被除数:

从上述示例运行的记录可以看到:

1、当分别输入数字60后,因为检测到数字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('++++++开始计算两数相除的商:++++++')     try:          num1 = eval(input('请输入被除数:'))         num2 = eval(input('请输入除数:'))         result = num1/num2     except (ZeroDivisionError, NameError) as e:         print('程序出现异常:%s,请重新开始!' % (e))     else:         print('商 = 被除数/除数 = %d/%d = %d' % (num1, num2, result))

2、示例运行:

 C:exception> python try_except_else.py ++++++开始计算两数相除的商:++++++ 请输入被除数:6 请输入除数:2 商 = 被除数/除数 = 6/2 = 3 ++++++开始计算两数相除的商:++++++ 请输入被除数:6 请输入除数:0 程序出现异常:division by zero,请重新开始! ++++++开始计算两数相除的商:++++++ 请输入被除数:

从上述示例运行的记录可以看到:

1、当分别输入数字62后,程序接着执行result = num1/num2语句,此时程序未发生异常,故直接跳转到else子句块(代码第10行)执行,输出正确的商值3;

2、当分别输入数字60后,程序接着执行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, continuereturn 语句,则 finally子句块将在执行 break, continuereturn 语句之前被执行。

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('++++++开始计算两数相除的商:++++++')     try:          num1 = eval(input('请输入被除数:'))         num2 = eval(input('请输入除数:'))         result = num1/num2         print('商 = 被除数/除数 = %d/%d = %d' % (num1, num2, result))     except (ZeroDivisionError, NameError) as e:         print('程序出现异常:%s,请重新开始!' % (e))     finally:         print('------本次计算结束!------')

2、示例运行:

 C:exception> python try_except_finally.py ++++++开始计算两数相除的商:++++++ 请输入被除数:6 请输入除数:2 商 = 被除数/除数 = 6/2 = 3 ------本次计算结束!------ ++++++开始计算两数相除的商:++++++ 请输入被除数:6 请输入除数:0 程序出现异常:division by zero,请重新开始! ------本次计算结束!------ ++++++开始计算两数相除的商:++++++ 请输入被除数:a 程序出现异常:name 'a' is not defined,请重新开始! ------本次计算结束!------ ++++++开始计算两数相除的商:++++++ 请输入被除数:

从上述示例运行的记录可以看到:

1、当分别输入数字62后,程序接着执行result = num1/num2语句和print()语句输出正确的商值3,此时程序未发生异常,故直接跳转到finally子句块(代码第11行)执行,输出提示信息(------本次计算结束!------);

2、当分别输入数字60后,程序接着执行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('++++++开始计算两数相除的商:++++++')     try:          num1 = eval(input('请输入被除数:'))         num2 = eval(input('请输入除数:'))         result = num1/num2     except (ZeroDivisionError, NameError) as e:         print('程序出现异常:%s,请重新开始!' % (e))     else:         print('商 = 被除数/除数 = %d/%d = %d' % (num1, num2, result))     finally:         print('------本次计算结束!------')

2、示例运行:

 C:exception> python try_except_else_finally.py ++++++开始计算两数相除的商:++++++ 请输入被除数:6 请输入除数:2 商 = 被除数/除数 = 6/2 = 3 ------本次计算结束!------ ++++++开始计算两数相除的商:++++++ 请输入被除数:6 请输入除数:0 程序出现异常:division by zero,请重新开始! ------本次计算结束!------ ++++++开始计算两数相除的商:++++++ 请输入被除数:a 程序出现异常:name 'a' is not defined,请重新开始! ------本次计算结束!------ ++++++开始计算两数相除的商:++++++ 请输入被除数:

从上述示例运行的记录可以看到:

1、当分别输入数字62后,程序接着执行result = num1/num2语句和print()语句输出正确的商值3,此时程序未发生异常,故直接跳转到else子句块(代码第10行)执行,输出提示信息(商 = 被除数/除数 = 6/2 = 3),最后跳转到finally子句块(代码第12行)执行,输出提示信息(------本次计算结束!------);

2、当分别输入数字60后,程序接着执行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 "", line 1, in      raise ValueError ValueError

二、抛出一个NameError异常实例(不带参数)示例

 >>> raise NameError() Traceback (most recent call last):   File "", line 1, in      raise NameError() NameError

三、抛出一个ZeroDivisionError异常实例(不带参数)示例

 >>> raise ZeroDivisionError('除数不能为零!') Traceback (most recent call last):   File "", line 1, in      raise ZeroDivisionError('除数不能为零!') ZeroDivisionError: 除数不能为零!

四、如果仅需确定是否引发了异常,但并不打算处理它,则可以使用更简单的 raise 语句形式重新引发异常

 >>> try: ...     raise NameError('这是NameError异常示例') ... except NameError: ...     print('一个异常出现了!') ...     raise  一个异常出现了! Traceback (most recent call last):   File "", line 2, in      raise NameError('这是NameError异常示例') NameError: 这是NameError异常示例

5.2 抛出一个自定义异常

开发者如果想抛出一个自定义异常,则首先必须先完成自定义异常类;然后再使用raise语句,象抛出一个内置异常一样抛出这个自定义异常。

下面示例如何抛出一个已经自定义好的自定义异常类MyException

 # 自定义异常类MyException class MyException(Exception):     def __init__(self, value):         self.value = value     def __str__(self):         return repr(self.value)  while True:     print('++++++开始计算两数相除的商:++++++')     try:          num1 = eval(input('请输入被除数:'))         num2 = eval(input('请输入除数:'))         if num2==0:             # 手动抛出一个自定义异常             raise MyException('不能除以0!')         result = num1/num2         print('商 = 被除数/除数 = %d/%d = %d' % (num1, num2, result))     except MyException as e:         print('程序出现MyException异常:%s,请重新开始!' % (e))     except NameError as e:         print('程序出现NameError异常:%s,请重新开始!' % (e))     except:         print('程序出现其他异常,请重新开始!')

运行结果:

 C:exception> python try_except_custom.py ++++++开始计算两数相除的商:++++++ 请输入被除数:6 请输入除数:0 程序出现MyException异常:'不能除以0!',请重新开始! ++++++开始计算两数相除的商:++++++ 请输入被除数:a 程序出现NameError异常:name 'a' is not defined,请重新开始! ++++++开始计算两数相除的商:++++++ 请输入被除数:

1、当分别输入数字60后,程序会接着执行到raise MyException('不能除以0!')语句,此时程序代码语句手动抛出一个异常(是自定义异常MyException),被except MyException as e:语句捕获到了,直接跳转到except MyException as e:子句块(代码第19行)执行:打印输出异常提示信息(程序出现MyException异常:'不能除以0!',请重新开始!)。此时程序并不会终止,会继续开始一个新的循环。

3、当输入字母a后,因为检测到字母a未被定义,程序发出一个异常(异常类型为NameError),被except NameError as e:语句捕获到了,直接跳转到except NameError as e:子句块(代码第21行)执行:打印输出提示信息(程序出现NameError异常:name 'a' is not defined,请重新开始!)。然后程序并不会终止,会继续开始一个新的循环。


6. 如何让程序输出更详细的异常信息

通过上述章节的介绍,我们已经可以熟练掌握在Python代码中使用多种组合try-except语句,来有效提高程序的健壮性,减少因出现异常而终止程序运行的情况发生。

6.1 面临的问题 - 使用捕获的异常类只能获取较少的异常信息

一、问题描述

我们现在已经可以在Python程序运行过程中出现异常时通过捕获的异常类输出相应的异常信息了。虽然可以通过一些技术手段来辅助更好显示异常信息,但这些异常信息相比Traceback信息要简单很多,不利于开发者定位出现异常的具体位置。

二、示例代码

 while True:     print('++++++开始计算两数相除的商:++++++')     try:          num1 = eval(input('请输入被除数:'))         num2 = eval(input('请输入除数:'))         result = num1/num2         print('商 = 被除数/除数 = %d/%d = %d' % (num1, num2, result))     except ZeroDivisionError as e:         print('程序出现ZeroDivisionError异常:%s,请重新开始!' % (e))     except NameError as e:         print('程序出现NameError异常:%s,请重新开始!' % (e))     except:         print('程序出现其他异常,请重新开始!')

三、示例运行

 C:exception> python try_except.py ++++++开始计算两数相除的商:++++++ 请输入被除数:6 请输入除数:0 程序出现ZeroDivisionError异常:division by zero,请重新开始! ++++++开始计算两数相除的商:++++++ 请输入被除数:a 程序出现NameError异常:name 'a' 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('++++++开始计算两数相除的商:++++++')     try:          num1 = eval(input('请输入被除数:'))         num2 = eval(input('请输入除数:'))         result = num1/num2         print('商 = 被除数/除数 = %d/%d = %d' % (num1, num2, result))     except:         print('---程序发生如下异常:---')         traceback.print_exc()         print('---请重新开始!---')

三、示例程序运行结果

 C:exception> python try_except_traceback.py ++++++开始计算两数相除的商:++++++ 请输入被除数:6 请输入除数:0 ---程序发生如下异常:--- Traceback (most recent call last):   File "c:exception/try_except_traceback.py", line 8, in      result = num1/num2 ZeroDivisionError: division by zero ---请重新开始!--- ++++++开始计算两数相除的商:++++++ 请输入被除数:a ---程序发生如下异常:--- Traceback (most recent call last):   File "c:/exception/try_except_traceback.py", line 6, in      num1 = eval(input('请输入被除数:'))   File "", line 1, in  NameError: name 'a' is not defined ---请重新开始!--- ++++++开始计算两数相除的商:++++++ 请输入被除数:

可以看到示例程序运行过程中,当出现输入数为字母a或第2个输入数为数字0时,程序发生异常,但并不会终止程序运行,同时也会在终端输出了的异常信息(Traceback信息):除了最后一行直接输出了异常类别和异常信息外,还在直接定位了出现异常的具体位置(文件名、代码行、出错语句)。

四、补充信息:

1、traceback.print_exc()直接打印出异常信息;

traceback.print_exc()中可以接收file参数,直接将异常信息写入到指定文件(如当前目录下的errorlog.txt文件)中。

 traceback.print_exec(file=open('errorlog.txt', 'w+'))

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 objecttraceback object中包含出错的行数、位置等数据。有了traceback object就可以通过Python内置库(traceback模块)来打印和格式化traceback的相关信息了。

下面示例通过sys.exc_info()函数来获取当前处理的异常的相关信息,然后通过traceback.print_exception()函数对这些异常数据进行整理输出。

二、示例代码:(try_except_systraceback.py)

 import sys, traceback  while True:     print('++++++开始计算两数相除的商:++++++')     try:          num1 = eval(input('请输入被除数:'))         num2 = eval(input('请输入除数:'))         result = num1/num2         print('商 = 被除数/除数 = %d/%d = %d' % (num1, num2, result))     except:         print('---程序发生如下异常:---')         exc_type, exc_value, exc_tb = sys.exc_info()         traceback.print_exception(exc_type, exc_value, exc_tb)         print('---请重新开始!---')   

三、示例运行结果:

 C:exception> python try_except_systraceback.py ++++++开始计算两数相除的商:++++++ 请输入被除数:6 请输入除数:0 ---程序发生如下异常:--- Traceback (most recent call last):   File "c:/exception/try_except_systraceback.py", line 8, in      result = num1/num2 ZeroDivisionError: division by zero ---请重新开始!--- ++++++开始计算两数相除的商:++++++ 请输入被除数:a ---程序发生如下异常:--- Traceback (most recent call last):   File "c:exception/try_except_systraceback.py", line 6, in      num1 = eval(input('请输入被除数:'))   File "", line 1, in  NameError: name 'a' 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程序更加健壮了。

希望本文能对您有所帮助! 喜欢的话就点个赞加关注支持一下哈:)

python异常处理_Python学习点滴04 - 学会异常处理(2)相关推荐

  1. python文件操作与异常处理_Python学习——文件操作和异常处理

    在上一篇文章中,我们介绍了 Python 的函数和模块,现在我们介绍 Python 中的异常和文件. 查看上一篇文章请点击:https://www.cnblogs.com/dustman/p/9963 ...

  2. 2019最新Python学习教程(Python视频教程_Python学习教程_Python学习路线):你心目中编程界的MVP是谁?

    2019最新Python学习教程(Python视频教程_Python学习教程_Python学习路线):你心目中编程界的MVP是谁?编程界的王者是渐落寞的Java还是大火的Python? 是不是你们也喜 ...

  3. [转载] python中异常处理的四个句子_Python学习笔记总结(四)异常处理

    参考链接: 在Python中定义清理动作 1.基础 try/except/else:[else是可选的]捕捉由代码中的异常并恢复,匹配except里面的错误,并执行except中定义的代码,后继续执行 ...

  4. python3中异常处理_python中的五种异常处理机制介绍|python3教程|python入门|python教程...

    https://www.xin3721.com/eschool/python.html 从几年前开始学习编程直到现在,一直对程序中的异常处理怀有恐惧和排斥心理.之所以这样,是因为不了解.这次攻pyth ...

  5. 菜鸟学python 哪吒_Python 学习之路 (前言)

    为什么要学Python 1,脚本语言本身很方便简洁,未来会有趋势 2,web 方向 3,运维方向 我是学静态语言出身的,java,毕业后从事android 应用开发,曾在工作期间学习过linux,想从 ...

  6. python不简单_Python学习并不简单!月薪过万也不易

    Python入门简单,精通不易 可以说,大部分编程语言都是入门简单,精通不易. 了解编程语言的基本语法很快,使用编程语言开发符合企业标准的项目很难.要想达到企业用人标准,需要花费非常多的时间去学习编程 ...

  7. python展望_python学习的一点随想与展望

    python学习的一点随想与展望 们的   找规律   如何   什么   bsp   学会   应用   span 第一次看见这学期课表的时候,我心里真的有些惶恐.尤其是高级语言程序设计,数据库,s ...

  8. python勾股定理_Python学习第128课——在Python中实现醉汉随机游走

    [每天几分钟,从零入门python编程的世界!] 这节我们在2D平面内实现随机游走.我们先把原理搞清楚,用代码实现这个原理. 原理分析: 我们想像在2D平面内有一个x轴和y轴组成的坐标系,有一个人他是 ...

  9. python大纲_python学习大纲

    知乎获赞无数的编程指南,介绍的不光是一门语言的入门,也是关于编程的入门,谈到了作为一名程序员,应该掌握的一些计算机知识. ------ 二.Python社区 强烈推荐Python Tip,有刷题挑战赛 ...

最新文章

  1. SSRS:服务器更名后,ReportingService无法使用和登录的解决办法
  2. Zabbix监控httpd服务
  3. 如何快速安全的插入千万条数据?
  4. JavaScript实现完整的ComplexNumber复数类(附完整源码)
  5. 经典C语言程序100例之三四
  6. u3d文件上传至服务器,unity 上传图片到云服务器
  7. Laravel笔记记录
  8. C++ std::iota递增
  9. 最近租房有点烦!技术人如何用Python找到称心如意的“小窝”?
  10. Windows Server 2016-DHCP服务器审核日志大小调整
  11. 实对称矩阵一定可以相似对角化
  12. Hadoop相关概念
  13. c++面试常见问题总结
  14. 孩子总是偷偷玩游戏,家长该怎么办?
  15. AI带你省钱旅游!精准预测民宿房源价格! ⛵
  16. java中的数组长度的计算
  17. RocketMQ详细配置与使用
  18. 移植正点原子linux内核
  19. android无障碍功能开发,威尼斯游戏-官网首页
  20. 算一下你来到这个世界多少天?

热门文章

  1. cctv系统车站服务器功能,CCTV系统
  2. 【Java线程】“打工人”初识线程池及自定义线程池实战
  3. 字符串匹配算法之KMP
  4. 对于超平面的理解[转载]
  5. 星级评价组件--引发对React组件的思考
  6. 【 全干货 】5 分钟带你看懂 Docker ! 1
  7. 名称节点和数据节点作用
  8. dynamic的一些使用心得
  9. 单行函数(数值函数)
  10. HBase1.2.3 数据模型