零基础学Python--------第9章 异常处理及程序调试
第9章 异常处理及程序调试
9.1 异常概述
在程序运行过程中,经常会遇到各种各样的错误,这些错误统称为“异常”。这些异常有的是由于开发者将关键字敲错导致的,这类错误多数产生的是SyntaxError:invalid syntax(无效的语法),这将直接导致程序不能运行。这类异常是显式的,在开发阶段很容易被发现。还有一类是隐式的,通常和使用者的操作有关。
实例01:模拟幼儿园分苹果
def division(): '''功能:分苹果''' print("\n=====================分苹果======================\n") apple = int(input("请输入苹果的个数:")) # 输入苹果的数量 children = int(input("请输入来了几个小朋友:")) result = apple//children # 计算每人分几个苹果 remain = apple-result*children # 计算余下几个苹果 if remain>0: print(apple,"个苹果,平均分给",children,"个小朋友,每人分",result,"个,剩下",remain,"个。") else: print(apple,"个苹果,平均分给",children,"个小朋友,每人分",result,"个。") if __name__ == '__main__': division() # 调用分苹果的函数
运行程序,当输入苹果和小朋友的数量都是10时:
请输入苹果的个数:10
请输入来了几个小朋友:10
10 个苹果,平均分给 10 个小朋友,每人分 1 个。
如果在输入数量时,不小心把小朋友的人数输成了0:
产生了ZeroDivisionError(除数为0错误)的根源在于算术表达式“10/0”中,0作为除数出现,所以正在执行的程序中被中断(第6行以后,包括第6行的代码都不会被执行)。
Python中还有很多异常。
异常 | 描述 |
NameError | 尝试访问一个没有声明的变量引发的错误 |
IndexError | 索引超出序列范围引发的错误 |
IndentationError | 缩进错误 |
ValueError | 传入错误 |
KeyError | 请求一个不存在的字典关键字引发的错误 |
IOError | 输入输出错误(如要读取的文件不存在) |
ImportError | 当import语句无法找到模块或from 无法在模块中找到相应的名称时引发的错误 |
AttributeError | 尝试访问未知的对象属性引发的错误 |
TypeError | 类型不合适引发的错误 |
MemoryError | 内存不足 |
ZeroDivisionError | 除数为0 引发的错误 |
9.2 异常处理语句
在程序开发时,有些错误并不是每次运行都会出现。
下面将详细介绍Python中提供的异常处理语句。
9.2.1 try...eccept
在Python中,提供了try...except语句捕获并处理异常。在使用时,把可能产生异常的代码放在try 语句块中,把处理结果放在except 语句块中,这样,当try 语句块中的代码出现错误时,就会执行except语句块中的代码,如果try 语句块中的代码没有错误,那么except 语句块将不会执行。具体的语法格式如下:
try: block1 except [ExceptionName [as alias]]: block2
参数说明:
- block1:表示可能出现错误的代码块。
- ExceptionName [as alias]:可选参数,用于指定要捕获的异常。其中,ExceptionName表示要捕获的异常名称,如果在其右侧加上as alias,则表示为当前的异常指定一个别名,通过该别名,可以记录异常的具体内容。
说明:在使用try...except语句捕获异常时,如果在except后面不指定异常名称,则表示捕获全部异常。
- block2:表示进行异常处理的代码块。在这里可以输出固定的提示信息,也可以通过别名输出异常的具体内容。
说明:使用try...except语句捕获异常后,当程序出错时,输出错误信息后,程序会继续执行。
实例02:模拟幼儿园分苹果(除数不能为0)
对“if __name__ =='__main__':”语句下面的代码进行修改,应用try...except语句捕获执行division()函数可能抛出的ZeroDivisionError(除数为零)异常,修改后的代码如下:
def division(): '''功能:分苹果''' print("\n=====================分苹果======================\n") apple = int(input("请输入苹果的个数:")) # 输入苹果的数量 children = int(input("请输入来了几个小朋友:")) result = apple//children # 计算每人分几个苹果 remain = apple-result*children # 计算余下几个苹果 if remain>0: print(apple,"个苹果,平均分给",children,"个小朋友,每人分",result,"个,剩下",remain,"个。") else: print(apple,"个苹果,平均分给",children,"个小朋友,每人分",result,"个。") if __name__ == '__main__': try: # 捕获异常 division() # 调用分苹果的函数 except ZeroDivisionError: # 处理异常 print("\n出错了 ~_~ ——苹果不能被0个小朋友分!")
执行以上的代码,输入苹果的数量为10,小朋友的人数为0时,将不再抛出异常:
目前,我们只处理了除数为0的情况,如果将苹果和小朋友的数量输入成小数或者不是数字会是什么结果呢?
可以看出,程序中要求输入整数,而实际输入的是小数,则抛出ValueError(传入的值错误)异常。要解决该问题,可以在实例02的代码中,为try...except语句再添加一个except语句,用于处理抛出ValueError异常的情况。修改后的代码如下:
def division(): '''功能:分苹果''' print("\n=====================分苹果======================\n") apple = int(input("请输入苹果的个数:")) # 输入苹果的数量 children = int(input("请输入来了几个小朋友:")) result = apple//children # 计算每人分几个苹果 remain = apple-result*children # 计算余下几个苹果 if remain>0: print(apple,"个苹果,平均分给",children,"个小朋友,每人分",result,"个,剩下",remain,"个。") else: print(apple,"个苹果,平均分给",children,"个小朋友,每人分",result,"个。") if __name__ == '__main__': try: # 捕获异常 division() # 调用分苹果的函数 except ZeroDivisionError: # 处理异常 print("\n出错了 ~_~ ——苹果不能被0个小朋友分!") except ValueError as e: # 处理ValueError异常 print("输入错误:",e) # 输出错误原因
再次运行程序:
多学两招:
在捕获异常时,如果需要同时处理多个异常也可以采用下面的代码实现:
try: # 捕获异常 division() # 调用分苹果的函数 except (ZeroDivisionError,ValueError) as e: # 处理异常 print("输入错误:",e) # 输出错误原因
即在except语句后面使用一对小括号将可能出现的异常名称括起来,多个异常名称之间使用逗号分隔。如果想要显示具体的出错原因,那么再加上as指定一个别名。
9.2.2 try...except...else 语句
在Python中国,还有另一种异常处理结构,它是try...except...else语句,也就是在原来try...except语句的基础上再添加一个else子句,用于指定当try语句块中没有发现异常时要执行的语句块。该语句块中的内容当try语句中发现异常,将不被执行。例如,实例02进行修改,实现当division()函数被执行后没有抛出异常时,输出文字“分苹果顺利完成...”。修改后的代码如下:
def division(): '''功能:分苹果''' print("\n=====================分苹果======================\n") apple = int(input("请输入苹果的个数:")) # 输入苹果的数量 children = int(input("请输入来了几个小朋友:")) result = apple//children # 计算每人分几个苹果 remain = apple-result*children # 计算余下几个苹果 if remain>0: print(apple,"个苹果,平均分给",children,"个小朋友,每人分",result,"个,剩下",remain,"个。") else: print(apple,"个苹果,平均分给",children,"个小朋友,每人分",result,"个。") if __name__ == '__main__': try: # 捕获异常 division() # 调用分苹果的函数 except ZeroDivisionError : # 处理异常 print("\n出错了 ~_~ ——苹果不能被0个小朋友分!") except ValueError as e: # 处理ValueError异常 print("输入错误:",e) # 输出错误原因 else: # 没有抛出异常时执行 print("分苹果顺利完成...")
执行以上代码:
9.2.3 try...except...finally 语句
完整的异常处理语句应该包含finally 代码块,通常情况下,无论程序中有无异常产生,finally 代码块中的代码都会被执行,其语法格式如下:
try: block1 except [ExceptionName [as alias]]: block2 fianlly: block3
对于try...except...finally 语句的理解并不复杂,它只是比try...except 语句多了一个finally 语句,如果程序中有一些在任何情形中都必须执行的代码,那么就可以将它们放在finally 代码块中。
说明:使用except 子句是为了允许处理异常。无论是否引起了异常,使用finally 子句都是可以执行清理代码。如果分配了有限的资源(如打开文件),则应将释放这些资源的代码放置在finally 代码块中。
例如,在对实例02进行修改,实现当division() 函数在执行时无论是否抛出异常,都输出文字“进行了一次分苹果操作”。修改后的代码如下:
def division(): '''功能:分苹果''' print("\n=====================分苹果======================\n") apple = int(input("请输入苹果的个数:")) # 输入苹果的数量 children = int(input("请输入来了几个小朋友:")) result = apple//children # 计算每人分几个苹果 remain = apple-result*children # 计算余下几个苹果 if remain>0: print(apple,"个苹果,平均分给",children,"个小朋友,每人分",result,"个,剩下",remain,"个。") else: print(apple,"个苹果,平均分给",children,"个小朋友,每人分",result,"个。") if __name__ == '__main__': try: # 捕获异常 division() # 调用分苹果的函数 except ZeroDivisionError : # 处理异常 print("\n出错了 ~_~ ——苹果不能被0个小朋友分!") except ValueError as e: # 处理ValueError异常 print("输入错误:",e) # 输出错误原因 else: # 没有抛出异常时执行 print("分苹果顺利完成...") finally: # 无论是否抛出异常都执行 print("进行了一次分苹果操作。")
执行程序:
至此,已经介绍了异常处理语句的try....except、try...except...else 和 try...except...finally 等形式。
异常处理语句的不同子句的执行关系
9.2.4 使用raise 语句抛出异常
如果某个函数或方法可能会产生异常,但不想在当前函数或方法中处理这个异常,则可以使用raise 语句在函数或方法中抛出异常。raise 语句的语法格式如下:
raise [ExceptionName[(reason)]]
其中,ExceptionName[(reason)]为可选参数,用于指定抛出的异常名称以及异常信息的相关描述。如果省略,就会把当前的错误原样抛出。
说明:ExceptionName[(reason)]参数中的“(reason)”也可以省略,如果省略,则在抛出异常时,不附带任何描述信息。
例如,修改实例02,加入限制苹果数量必须大于或等于小朋友的数量,从而保证每个小朋友都能至少分到一个苹果。
实例03:模拟幼儿园分苹果(每个人至少分到一个苹果)
在第5行代码“children = int(input("请输入来了几个小朋友:"))”的下方添加一个if 语句,实现当苹果的数量小于小朋友的数量时,应用raise 语句抛出一个ValueError 异常,接下来再在最后一行语句的下方添加except 语句处理ValueError 异常。
def division(): '''功能:分苹果''' print("\n=====================分苹果======================\n") apple = int(input("请输入苹果的个数:")) # 输入苹果的数量 children = int(input("请输入来了几个小朋友:")) if apple < children: raise ValueError("苹果太少了,不够分...") result = apple//children # 计算每人分几个苹果 remain = apple - result*children # 计算余下几个苹果 if remain>0: print(apple,"个苹果,平均分给",children,"个小朋友,每人分",result,"个,剩下",remain,"个。") else: print(apple,"个苹果,平均分给",children,"个小朋友,每人分",result,"个。") if __name__ == '__main__': try: # 捕获异常 division() # 调用分苹果的函数 except ZeroDivisionError : # 处理ZeroDivisionError异常 print("\n出错了 ~_~ ——苹果不能被0个小朋友分!") except ValueError as e: # ValueError print("\n出错了~_~ ——",e)
执行程序:
说明:在应用raise 抛出异常时,要尽量选择合理的异常对象,而不应该抛出一个与实际内容不相关的异常。流入,在实例03中,想要处理的是一个和值有关的异常,这时就不应该抛出一个IndentationError异常。
9.3 程序调试