错误及调试
标签(空格分隔):
错误处理
通常利用try...except... finally...
语句进行错误处理。
try
try: #在可能出错的地方添加`try`,如果出错就跳转到`except`
print('try.......')
r = 10/0
print('r = ', r)
except ZeroDivisionError as e: #在出现错误或异常后,由except捕捉,并处理。
print('except:', e)
finally: #上述过程结束后,finally语句一定会被执行。
print('finally......')
print('end')
如果需要捕捉多个异常,那么就需要使用多个except
进行捕获,分别进行处理。
调用栈
如果出现的错误一直没有被捕获,那么就会一直网上抛,最后被Python解释器捕获,打印一个错误信息,然后程序退出。
def foo(s):
return 10/int(s)
def bar(s):
return foo(s)*2
def main():
bar('0')
main()
解释器会逐层上抛错误,并且打印错误信息。
按照每个栈的信息寻找错误。
def main():
try:
bar('0')
except Exception as e:
logging.exception(e)
这样在出错后会打印信息并且执行完程序,而不会退出。
通过配置,logging还可以吧错误记录到日志文件里。
抛出错误
因为错误是class,捕获错误就是捕获该class的一个实例。
raise
就可以自己抛出一个错误,自己定义一个错误的class,并选择合适的继承关系。
class FooError(ValueError):
pass
def foo(s):
n = int(s)
if n==0:
raise FooError('invalid value : %s' % s)
return 10/0
foo('0')
另外一种方法:
def bar():
try:
foo('0')
except ValueError as e:
print('ValueError!')
raise
再出现错误之后,只是记录错误,但是在函数中无法处理,就继续上抛raise
该错误。
调试
assert
断言。
def foo(s):
n = int(s)
assert n!=0, 'n is zero!'
return 10/n
assert
第一项表达式如果是True就继续,否则后面就会出错,assert本身抛出AssertionError
:并且打印assert后面的内容。
在Python解释器中可以使用-0
参数关闭assert:
$ python -O err.py #关闭后,所有的assert语句都可以看成pass。
Traceback (most recent call last):
...
ZeroDivisionError: division by zero
logging
使用logging
是第三种方法,和assert相比,logging不会抛出错误,但是可以输出到文件。
import logging
s = '0'
n = int(s)
logging.info('n = %d' % n)
print(10/n)
这就是logging
的好处,它允许你指定记录信息的级别,有debug,info,warning,error
等几个级别,当我们指定level=INFO时,logging.debug就不起作用了。同理,指定level=WARNING后,debug和info就不起作用了。这样一来,你可以放心地输出不同级别的信息,也不用删除,最后统一控制输出哪个级别的信息。
pdb
第四种方法就是启用Python的pdb,让程序以单步方式运行,可以随时查看运行状态。
在调试器中使用
python -m pdb [文件.py]
就可以单步执行文件,
输入命令l
查看代码,
输入命令n
执行下一步,
命令p 变量名
可以查看变量。
q
结束调试,退出程序。
可以在程序中加入pdb.set_trace()
设置一个断点。可以用p
查看变量,c
继续运行。
单元测试
单元测试是用来对一个模块、一个函数或者一个类来进行正确性检验的测试工作。
比如对函数abs(),我们可以编写出以下几个测试用例:
- 输入正数,比如1、1.2、0.99,期待返回值与输入相同;
- 输入负数,比如-1、-1.2、-0.99,期待返回值与输入相反;
- 输入0,期待返回0;
- 输入非数值类型,比如None、[]、{},期待抛出TypeError。
把上面的测试用例放到一个测试模块里,就是一个完整的单元测试。
如果单元测试通过,说明我们测试的这个函数能够正常工作。如果单元测试不通过,要么函数有bug,要么测试条件输入不正确,总之,需要修复使单元测试能够通过。
import unittest
from mydict import Dict
class TestDict(unittest.TestCase):
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
为了编写单元测试,我们需要引入Python自带的unittest
模块,编写mydict_test.py
如上,
编写单元测试时,我们需要编写一个测试类,从unittest.TestCase
继承。
以test
开头的方法就是测试方法,不以test
开头的方法不被认为是测试方法,测试的时候不会被执行。
文档测试
…