一、异常
1、导引问题
在实际工作中,我们遇到的情况不可能是非常完美的,要解决Exception,我们要让程序做出合理的处理。
(1)异常机制的本质
异常指程序运行中出现的非正常现象,例如用户输入错误、除数为零、需要处理的文件不存在等等。
异常处理,是指程序出现问题时依然可以正确的执行剩余的程序,而不会因为异常而终止程序执行。
(2)异常的父子关系
碰到异常别害怕,反而应该高兴,解决的异常越多,我们会收获更多
I.首先先定位,在哪个文件里有问题;
II.追溯——Traceback 里面看到哪一行出现问题
III.最后会给一个原因,例如:ZeroDivisionError:division by zero
(3)解决异常问题的态度
I.不慌张,仔细看信息,定位错误,看清楚报的错误信息,定位发生错误的地方;
II.百度并查看解决方法,只需要复制错误描述这种关键信息;
III.以上两步都无法解决时,找老师和同学协助解决
2、try···一个 except 结构
try:
被监控的可能引发异常的语句块——可以写很多句
except BaseException[as e]:
异常处理语句块(如果有异常,要如何处理)
#try里面是监控语句,遇到异常就执行except部分,然后跳过继续执行下一步
print("step0")
try:
print("step1")
a = 3 / 0
print("step2")
except BaseException as e:
print("step3")
print("发生异常")
print(e)
print(type(e))
print("end!!!!!!!!")
'''
step0
step1
step3
发生异常
division by zero——对异常进行了描述
<class 'ZeroDivisionError'>
end!!!!!!!!
'''
#示例:循环输入数字,如果不是数字则异常处理,直到输入88,则结束循环
'
#无限循环用while True
while True:
a = input("请输入一个是数字:")
print("您输入的数字为:", a)
if int(a) == 88: #条件语句不能赋值,但是控制语句可以赋值
print("退出程序")
break
print("程序结束")
这个程序里面,当我输入了一个字符串时,出现了报错的情况,这个时候就体现了异常处理的中重要性
if int(a) == 88: #条件语句不能赋值,但是控制语句可以赋值
这个代码有个问题是,如果输入的不是整数,就会报错
ValueError: invalid literal for int() with base 10: 'ni'
改进后的代码
while True:
try:
a = input("请输入一个是数字:")
print("您输入的数字为:", a)
if int(a) == 88: #条件语句不能赋值,但是控制语句可以赋值
print("退出程序")
break
except BaseException as e:
print(e)
print("程序结束")
#输入了字母之后
#invalid literal for int() with base 10: 'ni'
3、try多个except结构
可以捕获所有的异常,但是要按照先子类后父类的顺序!
可以在最后增加BaseException,以免漏掉一些错误
#测试try···多个except结构
a = input("请输入一个被除数:")
b = input("请输入一个除数:")
c = float(a)/float(b)
print(c)
#ValueError: could not convert string to float: 'ee'
#可能会出现的错误有:输入的是字符串;输入的被除数为0
改进之后的代码如下:
try:
a = input("请输入一个被除数:")
b = input("请输入一个除数:")
c = float(a) / float(b)
print(c)
except ZeroDivisionError:
print("异常,被除数不能为0")
except ValueError:
print("异常,应输入数值")
except NameError:
print("变量不存在")
except BaseException as e:
print(e)
4、try···except···else结构
有异常的时候执行except,没有异常的时候执行else
执行完else以后再执行下一步
#try···except····else结构
try:
a = input("请输入一个被除数:")
b = input("请输入一个除数:")
c = float(a) / float(b)
except BaseException as e:
print(e)
else:
print(c)
5、try···except···finally结构
Finally块无论是否发生异常都会被执行,通常用来释放try块中申请的资源
#try···except····finally结构
try:
a = input("请输入一个被除数:")
b = input("请输入一个除数:")
c = float(a) / float(b)
except BaseException as e:
print(e)
else:
print(c)
finally:
print("我是finally中的病句,无论是否发生异常,都要执行哦!")
请输入一个被除数:15
请输入一个除数:0
float division by zero
我是finally中的病句,无论是否发生异常,都要执行哦!
try:
f = open("d:/dda.txt", "r")
content = f.readline()
print(content)
except:
print("文件未找到")
finally:
print("run in finally,关闭资源")
f.close()
print("程序执行已结束")
代码执行的结果如下:
文件未找到
run in finally,关闭资源
line 147, in <module>
f.close()
NameError: name 'f' is not defined
*由于在finally里面没找到f文件,报错之后,不再执行最后一步“程序已结束”
6、常见异常汇总和说明
*关于return
#return语句有两种作用:
(1)结束方法运行;
(2)返回值
***我们一般不把return放在异常处理结构中,而是放在方法最后
#常见异常的解决
(1)SyntaxError:语法错误;
(2)NameError:尝试访问一个没有申明的变量;
(3)ZeroDivisionError:除数为0错误;
(4)ValueError:数值错误;
(5)TpyeError:类型错误;
(6)AttributeError:访问对象的不存在的属性;
(7)IndexError:索引越界异常;
(8)KeyError:字典的关键字不存在;
7、traceback模块的使用
用来追溯,打印异常信息
#测试Trackback模块的使用
import traceback
try:
print("step1")
num = 1/0
except:
traceback.print_exc()
step1
Traceback (most recent call last):
File "D:/developer_tools/python/pythonProject/异常处理.py", line 202, in <module>
num = 1/0
ZeroDivisionError: division by zero
把错误信息详细得打印出来了!
import traceback
#a = "将异常信息输出到指定的文件里"
#b = a.center(50, "*")
以上两行代码不重要
try:
print("step2")
num = 1/0
except:
with open("d:/a.txt","a") as f: #用a的话,每次的异常信息可以添加,用w会清空原有文档的内容
traceback.print_exc(file=f) #括号里什么都不加的时候默认写在python中,当括号里写入文件地址的时候,就把异常写在文件里
8、自定义异常类
自定义异常一般都是运行时异常,通常继承Exception或其子类即可。明明一般以Error、Exception为后缀;
##测试自定义异常类
class AgeError(Exception): #继承Exception类
def __init__(self, errorInfo): #通常需要重写一下构造器,固定的模式
Exception.__init__(self) #利用父类的初始函数
self.errorInfo = errorInfo
def __str__(self):
return str(self.errorInfo) + "年龄错误!应该在1-150之间"
#由于errorInfo 传入的是age,age又用int写为整数,说到底是个数字,所以将其格式转化为字符串,字符串进行相加,返回更全面的信息
在自定义异常类时,前面的代码都一样,主要就是self.errorInfo这里不同
a = "测试代码"
b = a.center(50, "#")
print(b)
if __name__ == "__main__": #如果为True的话,则模块是作为独立文件运行的,可以测试代码
age = int(input("输入一个年龄:"))
if age < 1 or age > 150:
raise AgeError(age) #抛出异常的意思,这里的函数必须要传一个参数
else:
print("正常的年龄", age)
'''
输入一个年龄:200
Traceback (most recent call last):
File "D:/developer_tools/python/pythonProject/异常处理.py", line 249, in <module>
raise AgeError(age)
__main__.AgeError: 200年龄错误!应该在1-150之间
'''
二、with上下文管理器
finally块由于是否发生异常都会执行,通常我们用它来释放资源。另一种方法是通过with进行上下文管理,更方便的实现释放资源的操作
语法结构:
with context__expr[as var]:
语句块
with上下文管理器可以自动管理资源,在with代码块执行完毕后自动还原进入该代码之前的现场或上下文。不论何种原因跳出with块,不论是否有异常,总能保证资源正常释放。极大简化了工作,在文件操作、网络通信相关的场合非常常用。
#测试with上下文管理,并不是用来取代try···except···finally结构的,是为了方便我们进行文件管理,网络通信时的开放
with open("d:/dda.txt", "r") as f:
content = f.readline()
print(content)
print("程序执行已结束")
#出现了报错的情况,是因为文件不存在
三、测试调试
1、断点
进行调试的核心是设置断点——程序运行到此处,暂时挂起,停止执行。我们可以详细在此观察程序的运行情况,方便做出进一步的判断。
(1)设置断点:
1>在行号后面单击即可增加断点;
2>在断点上再单击即可取消断点。
(2)通过debug来找bug,进入调试断点视图
(3)视图中的按钮含义如下:
<1>step over :若当前执行的是一个函数,则会把这个函数当成整体一步执行完,不会进入到这个函数内部;
<2>step into:若是当前执行的是一个函数,则会进入这个函数内部;
<3>step out :当单步执行到子函数内,用step out 就可以执行完子函数剩余下部分,并返回上一层函数
<4>run to cursor: 把光标作为一个断点,可以直接跳到光标所指向的地方
def aa():
print("run in aa() start!")
print("step1")
num1 = 3
num2 = num1 * 4
nume3 = num2 * 5
print("step2")
print("run in aa() end!!!!!!")
if __name__ == "__main__":
print("main:step1")
aa() *在这里设置断点
print("main:step2")
print("main:end!!!!!!")
'''
点击step over之后,会显示:
main:step1
run in aa() start!
step1
step2
run in aa() end!!!!!!
把aa()方法当做一个整体直接执行完
'''
在Debugger里面的Variables 下面有个加号是用来观察变量的,点击加号输入要观察的变量,可以看到没进行一步,变量的数值
(1)step over 直接将方法aa()全部执行完
(2)step into 跳入到aa()方法里,点step into一下操作一步
(3)step out 将方法aa()剩下的步骤全部执行完,最好是在方法aa()里面再设置一个断点,这样就比较好看出来效果了
(4)run to cursor 运行到光标处,用的不太多