Python实战基础19-异常处理及程序调试

1、异常概述

在程序运行过程中,经常会遇到各种各样的错误,这些错误统称为“异常”。

这些异常有的是由于开发者将关键字敲错,这类错误产生的是SyntaxError:invalid syntax(无效语法),这将直接导致程序不能运行。这类异常是显式的,在开发阶段很容易被发现。

还有一类是隐式的,通常和使用者的操作有关。

Python中常见的异常
异常 描述
NameError 尝试访问一个没有声明的变量引发的错误
IndexError 索引超出序列范围引发的错误
IndentationError 缩进错误
ValueError 传入的值错误
KeyError 请求一个不存在的字典关键字引发的错误
IOError 输入输出错误(如要读取的文件不存在)
ImportError 当import语句无法找到模块或from无法在模块中找到相应的名称时引发的错误
AttributeError 尝试访问未知的对象属性引发的错误
TypeError 类型不合适引发的错误
MemoryError 内存不足
ZeroDivisionError 除数为0引发的错误

1.1 模拟幼儿园分苹果

【任务描述】

幼儿园每天都要分苹果,老师每次口头计算都比较麻烦,请定义一个模拟分苹果的函数division()。在改函数中,要求输入苹果的数量和小朋友的数量,然后应用除法算式计算分配的结果(不能有小数),输出每人分配的苹果树和剩余的苹果数。

def division():
    '''功能:分苹果'''
    print('\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()

1.2  模拟幼儿园分苹果(除数不为0) 

【任务描述】

在1.1中写出了分苹果的函数,正常情况下可以运行,但是在输入小朋友人数为0的时候程序报错,这是不允许的,希望通过异常捕获的方法,捕捉这个异常。

def division():
    '''功能:分苹果'''
    print('\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个小朋友分!")

运行结果: 

Python实战基础19-异常处理及程序调试_第1张图片

2、异常处理语句

在程序开发时,有些错误并不是每次运行都会出现。例如1.1,只要输入的数据符合程序的要求,程序就可以正常运行,否则将抛出异常并停止运行。

假设在输入苹果的数量时,输入了23.5,那么将抛出如下图所示的异常

Python实战基础19-异常处理及程序调试_第2张图片

这时,就需要在开发程序时对可以出现异常的情况进行处理。 

2.1 try...except语句

语法格式:

try:

        block1

except [exceptionname [as alias]]:

        block2

参数说明:

block1:表示可能出现的错误的代码块

exceptionname [as alias]:可选参数,用于指定要捕获的异常。其中,ExceptName表示要捕获的异常名称,如果在其右侧加上as alias,则表示为当前的异常指定一个别名,通过改别名,可以记录异常的具体内容。

说明:

  • 在使用try...except语句捕获异常时,如果在except后面不指定异常名称,则表示捕获全部异常。
  • block2:表示进行异常处理的代码块。在这里可以输出固定的提示信息,也可以通过别名输出异常的具体内容。
  • 使用try...except语句捕获异常后,当程序出错时,输出错误信息后,程序会继续执行。

Python实战基础19-异常处理及程序调试_第3张图片

从上图能看出,输入了小数,抛出ValueError(传入值的错误)异常。要解决改问题,可以在1.2代码中为try...except语句在添加一个except语句,用于处理抛出ValueError异常的情况。

def division():
    '''功能:分苹果'''
    print('\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:
        print('输入错误',e)

运行结果:

Python实战基础19-异常处理及程序调试_第4张图片

多学两招:

在捕获异常时,如果需要同时处理多个异常也可以采用下面的代码实现:

try:
    division()
except (ZeroDivisionError,ValueError) as e:
    print("输入错误",e)

即在except语句后面使用一对小括号将可能出现的异常名称括起来,多个异常名称之间使用逗号隔开。如果想要显示具体的出错原因,那么再加上as指定一个别名。 

2.2 try...except...else语句

语法格式:

try:

        # 代码段1

        pass

except name:

        # 代码段2

        pass

else:

        # 代码段3

        pass

就是再原来try...except语句的基础上再添加了一个else子句,用于指定当try语句块中没有发现异常时要执行的语句块。该语句块中的内容当try语句中发现异常时,将不被执行。

例如,对2.1进行修改,实现当division()函数被执行后没有抛出异常时,输出文件”分苹果顺利完成!!”。

def division():
    '''功能:分苹果'''
    print('\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:
        print('输入错误',e)
    else:
        print('分苹果顺利完成!!')

 Python实战基础19-异常处理及程序调试_第5张图片

2.3 try...except...finally语句

完整的异常处理语句应该包含finally代码块,通常情况下,无论程序中有无异常产生,finally块中的代码都会被执行。

语法格式:

try:

        block1

except [exceptionname [as alias]]:

        block2

finally:

        block3

对于try...except...finally语句的理解并不复杂,它只是比try...except语句多了一个finally语句,如果程序中有一些在任何情形中都必须执行的代码,那么就可以将它们放在finally代码块中。

说明:

  • 使用except子句是为了允许处理异常。无论是否引发了异常,使用finally子句都可以执行清理代码。如果分配了有限的资源(如打开文件),则应将释放这些资源的代码放置在finally代码块中。

例如对2.2进行修改,实现当division()函数在执行时无论是否抛出异常,都输出文字“进行了一次分苹果操作”。

def division():
    '''功能:分苹果'''
    print('\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:
        print('输入错误',e)
    else:
        print('分苹果顺利完成!!')
    finally:
        print('进行了一次分苹果操作')

运行结果:

 Python实战基础19-异常处理及程序调试_第6张图片

 Python实战基础19-异常处理及程序调试_第7张图片

2.4 使用raise语句抛出异常

如果某个函数或方法可能会产生异常,但不想在当前函数或方法中处理这个异常,则可以使用raise语句在函数或方法中抛出异常。

语法格式:

raise [ExceptionName[(reason)]

其中,ExceptionName[(reason)]为可选参数,用于指定抛出的异常名称以及异常信息的相关描述。如果省略,就会把当前的错误原样抛出。

说明:ExceptionName[(reason)]参数中的“(reason)”也可以省略,如果省略,那么在抛出异常时,不附带任何描述信息。

2.5 模拟幼儿园分苹果(使用raise抛出异常)

【任务描述】

在1.2中我们写处理分苹果的函数,现在学校要求小朋友至少分到一个苹果,如果每个小朋友没有平均分到一个苹果则抛出错误,并把“苹果太少,不够分!!!打印在屏幕上”。

def division():
    '''功能:分苹果'''
    print("\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, "个,剩下", remain, "个")
if __name__ == "__main__":
    try:
        division()
    except ZeroDivisionError:
        print("\n出错!!!苹果不能够被0个小朋友分")
    except ValueError as e:
        print("出错了",e)

运行结果:

Python实战基础19-异常处理及程序调试_第8张图片

说明:在应用raise抛出异常时,要尽量选择合理的异常对象,而不应该抛出一个与实际内容不相关的异常。 

3、程序调试

在程序开发过程中,免不了会出现一些错误,有语法方面的,也有逻辑方面的。对于语法方面的好检测,因为程序会直接停止,并且给出错误的提示。

对于逻辑错误就不太容易发现了,因为程序可能会一直执行下去,但结果是错误的。

3.1 使用自带的IDLE进行程序调试

多数的集成开发工具都提供了程序调试功能。

例如,我们一直使用的IDLE,也提供了程序调试功能。使用IDLE进行程序调试的基本步骤如下:

(1)打开IDLE(Python Shell),在主菜单上选择“Debug”→“Debugger"菜单项,将打开Debug Control对话框(此时该对话框时空白的),同时Python Shell窗口中将显示”[DEBUG ON]“(表示以及处于调试状态。如下图:

Python实战基础19-异常处理及程序调试_第9张图片

Python实战基础19-异常处理及程序调试_第10张图片

Python实战基础19-异常处理及程序调试_第11张图片 (2)在Python Shell窗口中,选择”File“ → ”Open“菜单项,打开要调试的文件,这里我打开之前写的模拟幼儿园分苹果.py文件,然后添加需要的断点。 

Python实战基础19-异常处理及程序调试_第12张图片

说明:断点的作用:设置断点后,程序执行到断点时就会暂时中断执行,程序可以随时继续。

添加断点的方法是: 在想要添加断点的行上,单击鼠标右键,在弹出的快捷菜单中选择”Set Breakpoint“菜单项。添加断点的行将以黄色底纹标记,如下图:

Python实战基础19-异常处理及程序调试_第13张图片

说明:如果想要删除已经添加的断点,可以选中已经添加断点的行,然后单击鼠标右键,在弹出的快捷菜单中选中”Clear Breakpoint“菜单项。

 (3)添加所需的断点(添加断点的原则是:程序执行到这个位置时,想要查看某些变量的值,就在这个位置添加一个断点)后,按下快捷键【F5】,执行程序,这时Debug Control对话框中将显示程序的执行信息,选中Globals复选框,将显示全局变量,默认只显示局部变量。此时的Debug Control对话框如下图:

Python实战基础19-异常处理及程序调试_第14张图片

(4)在上图所示的调试工具栏中,提供了5个工具按钮。这里单击“Go”按钮继续执行程序,直到所设置的第一个断点。由于在py文件中,第一个断点之前需要获取用户的输入,所以需要在Python Shell窗口中输入苹果和小朋友的数量。输入后Debug Control窗口的数据将发生变化,如下图:

Python实战基础19-异常处理及程序调试_第15张图片

说明:在调试工具栏中的5个按钮的作用为:Go按钮用于执行跳至断点操作;Step按钮用于进入要执行的函数;Over按钮表示单步执行;Out按钮表示跳出所在的函数;Quit按钮表示结束调试。

(5)继续单击“Go”按钮,将执行到下一个断点,查看变量的变化,直到全部断点都执行完毕。

程序调试完毕后,可以关闭Debug Control窗口,此时在Python Shell窗口将显示“[DEBUG OFF]"(表示已经结束调试)。 

Python实战基础19-异常处理及程序调试_第16张图片

3.2 使用assert语句调试程序

Python提供了assert语句来调试程序。assert的中午意思是断言,它一般用于对程序某个时刻必须满足的条件进行验证。

基本语法:

assert expression [,reason]

参数说明:

expression:条件表达式,如果该表达式的值为真时,什么都不做,如果为假时,则抛出AssertionError异常。

Reason:可选参数,用于对判断条件进行描述为了以后更好地知道哪里出现了问题。

你可能感兴趣的:(python基础,python,开发语言)