异常处理
-
概述:在程序运行过程中,总会遇到到各种各样的错误,有的错误是编程代码有问题造成的,这种错误通常称为BUG,BUG是必须修复的。有的错误时用户输入造成的,这种错误可以通过检查用户的输入来做响应处理。还有一种错误是完全无法再程序运行过中预测的,比如写文件的时候,磁盘满了,写不进去了。在比如从网络中抓取数据,突然断网了,通常这种情况称为异常,在程序中必须要处理的,否则程序会应为各种问题而结束。
-
遇到错误,最原始的方式解决是事先约定一个错误代码,这样就知道是否有错误,在操作系统提供的调用中非常常用
-
缺陷:
1、函数本身返回的数据与错误代码会混在一起,需要大量的判断是否出错
2、一旦出错,要一级一级上报,直到某个函数可以处理
‘’'try……except……else语句
格式
try:
语句t
except 错误表示码1 as e:
语句1
except 错误表示码2 as e:
语句2
……
except 错误表示码n as e:
语句n
else:
语句e
作用:用来检测“语句t”中的错误,从而让except语句捕获异常并处理
逻辑:
1、如果try后的“语句t”执行时发生异常,就跳回到执行try并执行一个匹配该异常的except子句,异常处理结束,就结束整个try语句(除非处理异常时又引发新的异常)
2、如果try后的“语句t”执行时发生异常,但是却没有匹配的except子句,异常提交到上一级try,或者到程序的最上层
3、如果try后的“语句t”执行时没有发生异常,就不会匹配except子句,如果有else语句,则执行“语句e”,最后结束这个try语句
try……except……else语句
- 格式
try:
语句t
except 错误表示码1 as e:
语句1
except 错误表示码2 as e:
语句2
……
except 错误表示码n as e:
语句n
else:
语句e - 作用:用来检测“语句t”中的错误,从而让except语句捕获异常并处理
- 逻辑:
1、如果try后的“语句t”执行时发生异常,就跳回到执行try并执行一个匹配该异常的except子句,异常处理结束,就结束整个try语句(除非处理异常时又引发新的异常)
2、如果try后的“语句t”执行时发生异常,但是却没有匹配的except子句,异常提交到上一级try,或者到程序的最上层
3、如果try后的“语句t”执行时没有发生异常,就不会匹配except子句,如果有else语句,则执行“语句e”,最后结束这个try语句
异常处理(try…except…finally语句)
- 格式
try:
语句t
except 错误表示1 as e:
语句1
except 错误表示2 as e:
语句1
……
except 错误表示n as e:
语句n
finally:
语句f - 作用
作用:无论try语句是否发生异常,都将执行“语句f”
调试
写好的代码能直接运行的概率非常小,总会在不经意间出现各种各样的BUG,有的BUG很简单,看看错误提示就能修改,但是有的BUG很复杂,需要一些调试的手段来发现并解决错误
print调试
print是最简单最原始的调试方式
- 缺点:将来得删除调试的print,运行结果也会包含一些垃圾信息
断言
- 使用:凡是用print来辅助调试的地方,都可以换成断言(assert)语句
- 逻辑:当程序执行到assert语句时,首先计算第一个表达式的值,如果表达式的值为真则继续向下运行,否则断言失败,assert语句会抛出AssertionError异常,异常的信息为第二个表达式的值
- 缺点:如果将所有的print换成assert也好不到哪里去
- 优点:在启动程序可以通过命令添加-0参数关闭assert
def func(x):
assert x != 0, "x is zero"
return 10 / x
func(0)
logging 调试
- 使用:把print替换成logging也可以,和assert相比logging不会抛出错误,把错误输入到文件
import logging
#配置输出级别
logging.basicConfig(level=logging.ERROR)
'''
CRITICAL = 50
FATAL = CRITICAL
ERROR = 40
WARNING = 30
WARN = WARNING
INFO = 20
DEBUG = 10
NOTSET = 0
'''
def func(x):
logging.error("x = %d"%(x))
return 10 / x
func(0)
pdb自定义开始断点
pdb是一个基于命令行的调试工具,只需要引入内置的pdb模块,并运行set_trace函数就可以触发调试器。通常将这两个操作写在一行中便于将其注释。
命令 简写命令 作用
break b 设置断点
continue c 继续执行程序,直到下一个断点或调用点
list l 查看当前行的代码段
step s 进入函数
return r 执行代码直到从当前函数返回
quit q 中止并退出
next n 执行下一行
print p 打印变量的值
help h 帮助
args a 查看传入参数
回车 重复上一条命令
break b 显示所有断点
break lineno b lineno 在指定行设置断点
break file:lineno b file:lineno 在指定文件的行设置断点
clear num 删除指定断点
bt 查看函数调用栈帧
e = 0
print('----------------------0')
#自定义开始断点
pdb.set_trace()
f = 5
print(1 / e)
# 输出
"""
----------------------0
> e:\python1903\python 基础\day15\code\7、调试(pdb的自定义开始断点).py(11)<module>()
-> f = 5
(Pdb)
""""
IDE断点调试
测试
函数的单元测试
- 单元测试
- 作用:用来对一个函数、一个类或者一个模块来进行正确性检测的工具
- 意义:
1、假设对函数的代码进行了修改,只需在跑一次单元测试,如果通过,说明此时的修改不会对函数原有的行为造成影响。如果不通过,说明此次的修改与原有行为不一致,要么修改代码,要么修改测试
2、确保一个程序模块的行为符合设计的测试用例,在将来修改的时候可以极大的保证该模块依然正确 - 结果:
1、单元测试通过:说明测试的这个函数或者类能够正常工作
2、单元测试不通过:要么有BUG,要么测试条件不正确
# calculater模块
def mySum(x, y):
ret = x + y + 1
a = 1
return ret
def mySub(x, y):
ret = x - y
return ret
# main 模块
from calculater import mySum, mySub
if __name__ == "__main__":
ret1 = mySum(1, 2)
if ret1 == 3:
print("ret1 = %d"%ret1)
#认为正确
ret2 = mySum(3, 4)
if ret2 == 8:
print("ret2 = %d"%ret2)
# test模块
#测试模块
import unittest
from calculater import mySum, mySub
#定义测试类
class Test(unittest.TestCase):
#测试开始之前调用
def setUp(self):
print("开始测试……")
# 测试结束之后调用
def tearDown(self):
print("结束测试……")
#测试函数名:test__待测试函数名
#注意:其实也可以不是待测试的函数名
#本质:只要是以test__开头的函数都会被执行
def test__mySum(self):
self.assertEqual(mySum(2, 2), 4)
def test__mySub(self):
self.assertEqual(mySub(4, 2), 2)
if __name__ == "__main__":
unittest.main()
对类进行单元测试
- myDict模块
class MyDict(dict):
def __init__(self, *args, **kwargs):
print("----------1")
super().__init__(*args, **kwargs)
def __setattr__(self, key, value):
print("-----------2")
self[key] = value
def __getattr__(self, item):
try:
print("--------3")
return self[item]
except KeyError as e:
raise AttributeError("dict not have the key")
def run(self):
pass
- test 模块
import unittest
from myDict import MyDict
#定义测试类
#类名:Test待测试类名
class TestMyDict(unittest.TestCase):
def setUp(self):
print("开始测试")
def tearDown(self):
print("结束测试")
# # 实例化阶段
def test_init(self):
d = MyDict(a=1, b=2)
# 断言
self.assertEqual(d.a, 1)
self.assertEqual(d.b, 2)
self.assertTrue(isinstance(d, dict))
# #
# # 测试设置属性
def test__key(self):
d = MyDict()
d["key"] = 1
self.assertTrue("key" in d)
self.assertEqual(d["key"], 1)
def test__attr(self):
d = MyDict()
d.key = 1
self.assertTrue("key" in d)
self.assertEqual(d["key"], 1)
#判断
def test__keyerror(self):
d = MyDict()
#通过d["key"]访问不存在的属性抛出KeyError异常
with self.assertRaises(KeyError):
value = d["key"]
def tset__attrerror(self):
d = MyDict()
# 通过d.key访问不存在的属性抛出AttributeError异常
with self.assertRaises(AttributeError):
value = d.key
#对象函数的测试
def test__run(self):
d = MyDict()
d.run()
if __name__ == "__main__":
unittest.main()
doctest模块
- 该模块可以直接提取注释中的代码并执行测试
import doctest
def mySum(x, y):
# doctest严格按照python交互式命令的输入和输出来判断测试结果是否正确
'''
>>> print(mySum(1, 2))
3
'''
return x + y + 1
if mySum(1, 2) == 3:
print("bingle is a good man")
if mySum(1, 1) == 3:
print("bingle is a nice man")
# 提取注释中代码执行
doctest.testmod()