第八章-错误调试
1 错误处理
1.1 异常捕捉
在Python中有try..except..else..finally用于捕捉异常和处理异常
try后面接需要处理异常的语句
except后面是对异常进行处理的, 具体有两个形式
1) 方法一
except 异常名字 as e:
print(e)
2)方法二
except:
print("出现异常")
else是当没有出现异常的时候执行该体内的语句
finally是不管出不出现异常都会执行
异常都是继承自BaseException
except可以有多个, 但是顺序很重要, 如果前面的是父类的异常, 那么在之后的子类的异常就处理不到了
因此先写类型小的异常, 再写类型大的异常
try..except还可以捕捉该语句块中可以出现调用关系内某个语句发生的异常
具体的演示实例如下
try:
print('try...')
r = 10 / int('2')
print('result:', r)
except ValueError as e:
print('ValueError:', e)
except ZeroDivisionError as e:
print('ZeroDivisionError:', e)
else:
print('no error!')
finally:
print('finally...')
print('END')
1.2 抛出异常
有的时候异常不一定非要在本位置处理, 可以抛出异常给上一级处理
抛出异常用raise
具体的使用方法
raise 异常名字(需要传递的异常信息)
可以通过定义类的方式定义一个自定义的异常, 自定义错误类需要继承一个错误
class FooError(ValueError):
pass
def foo(s):
n = int(s)
if n==0:
raise FooError('invalid value: %s' % s)
return 10 / n
foo('0')
另外在抛出异常之后, 可以在接受位置处理异常, 这样并不是冗余重复, 有的是为了集中处理异常
1.3 记录错误
可以通过logging模块来讲错误信息保存在日志文件中
这样函数不仅记录了错误信息, 程序就算发生了异常也可以正常运行
具体使用格式
import logging
...
logging.exception(异常对象)
具体代码如下
import logging
def foo(s):
return 10 / int(s)
def bar(s):
return foo(s) * 2
def main():
try:
bar('0')
except Exception as e:
logging.exception(e)
main()
print('END')
2 调试
2.1 断言
断音使用关键字assert
断言后面跟一个判断表达式
判断表达式为True表示能够正常执行, 为False就会出现AssertionError异常
断言判断表达式后面可以接一个提示信息, 用于出现异常的情况, 将该信息放到AssertionError后, 这俩之间用逗号分隔
具体格式如下
assert 判断表达式, 异常提示字符串
如果assert过多, 可以在执行py文件的时候加入-o参数来忽略断言
python3 -O py文件
2.2 logging
在logging模块中还可以记录不同级别的信息
logging的信息有debug, info, warning, error四种级别的信息
可以通过
logging.basicConfig(level=logging.级别名字)
来指定输出的级别信息, 设置之后在该级别前面的级别的信息就不起作用了
3 单元测试
单元测试是一种测试驱动开发的模式
可以确保程序模块符合测试用例
在模块修改的时候也可以在重新运行一下单元测试, 很方便
Python中的unittest模块就是编写单元测试的
单元测试的类名, 方法名(尤其是)都最好是以test开头
测试的类需要继承 unittest.TestCase
继承之后会有两个特殊地函数 setUp() 和 tearDown() 函数
这两个函数分别是在测试前和测试后执行
主要适用于在开始前后的准备和结束代码
下面是模拟dict的代码
class Dict(dict):
def __init__(self, **kw):
super().__init__(**kw)
def __getattr__(self, key):
try:
return self[key]
except KeyError:
raise AttributeError(r"'Dict' object has no attribute '%s'" % key)
def __setattr__(self, key, value):
self[key] = value
下面是测试该代码的单元测试
import unittest
from mydict import Dict
class TestDict(unittest.TestCase):
def setUp(self):
print('setUp...')
def tearDown(self):
print('tearDown...')
def test_init(self):
d = Dict(a=1, b='test')
self.assertEqual(d.a, 1)
self.assertEqual(d.b, 'test')
self.assertTrue(isinstance(d, dict))
def test_key(self):
d = Dict()
d['key'] = 'value'
self.assertEqual(d.key, 'value')
def test_attr(self):
d = Dict()
d.key = 'value'
self.assertTrue('key' in d)
self.assertEqual(d['key'], 'value')
def test_keyerror(self):
d = Dict()
with self.assertRaises(KeyError):
value = d['empty']
def test_attrerror(self):
d = Dict()
with self.assertRaises(AttributeError):
value = d.empty
if __name__ == '__main__':
unittest.main()
4 文档测试
在定义某些类的时候, 可以通过'''说明信息'''来形成__doc__
可以在该部分写入一些测试代码, 类似于交互式环境的代码
通过Python内置的文档测试, 可以运行那些测试代码, 来更加快速的检验代码的正确性
具体写入的代码中如果输出结果较多, 可以用...来代替
具体如下
class Dict(dict):
'''
Simple dict but also support access as x.y style.
>>> d1 = Dict()
>>> d1['x'] = 100
>>> d1.x
100
>>> d1.y = 200
>>> d1['y']
200
>>> d2 = Dict(a=1, b=2, c='3')
>>> d2.c
'3'
>>> d2['empty']
Traceback (most recent call last):
...
KeyError: 'empty'
>>> d2.empty
Traceback (most recent call last):
...
AttributeError: 'Dict' object has no attribute 'empty'
'''
def __init__(self, **kw):
super(Dict, self).__init__(**kw)
def __getattr__(self, key):
try:
return self[key]
except KeyError:
raise AttributeError(r"'Dict' object has no attribute '%s'" % key)
def __setattr__(self, key, value):
self[key] = value
if __name__=='__main__':
import doctest
doctest.testmod()
运行该代码后如果没有输出, 则一切正常
文档测试只会在命令直接运行时才会执行, 正常导入模块是不会执行的