错误处理:
程序运行的过程中如果发生了错误,就可以事先约定返回一个错误代码,但是用错误码表示是否出错十分的不方便,所以高级语言通常都内置了一套try...except...finally
的错误处理机制,Python也有这样的错误处理机制:
try:
print('try...')
r = 10 / 0
print('result:', r)
except ZeroDivisionError as e: # try运行过程中如果哪一行出错,就不会执行后续代码,直接跳转到这个代码块
print('except:', e)
except ZeroDivisionError as e: # 可以有多个except来捕获不同的错误
print('ZeroDivisionError:', e)
else: #当没有错误发生的时候,会执行else语句块
print('no error!')
finally: # 如果有finally语句块,就不管对错一定会执行这段代码
print('finally...')
print('END')
Python的错误是class,所有的错误都继承自BaseException
,所以每次捕获一个类型的错误,也会捕获这个类型错误的所有子类:
点击这里看错误类型和继承关系
记录错误使用logging
模块:
既然我们能捕获错误,那么我们就可以把错误堆栈记录下来,事后分析错误原因,同时让程序继续执行下去,Python内置的logging模块就可以记录错误信息。
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')
logging可以通过配置把错误记录到日志文件里面
抛出错误:
捕获错误就是捕获到一个错误class的一个实例,所以错误都是有意创建并抛出的,所以我们也可以使用自己编写的函数抛出错误:
class FooError(ValueError):
pass
def foo(s):
n = int(s)
if n==0:
raise FooError('invalid value: %s' % s)
return 10 / n
foo('0')
这里注意尽量使用Python的内置错误类型
调试:
第一种方法:print
调试的最简单办法就是用print()
打印出来看看,但是这样运行结果里面会包含很多垃圾信息
第二种方法:断言
def foo(s):
n = int(s)
assert n != 0, 'n is zero!' # 这里断言的意思是n应该不等于0,否则就抛出AssertionError的错误,并打印后面的‘n is zero'
return 10 / n
def main():
foo('0')
这样输出结果还是会有很多的垃圾信息,在启动Python解释器的时候可以用-0
来关闭assert:python -O err.py
这样关闭后,可以把所有的assert语句看成是pass
第三种方法:logging
logging不会输出错误,而且可以输出到文件:
import logging
logging.basicConfig(level=logging.INFO)
s = '0'
n = int(s)
logging.info('n = %d' % n)
print(10 / n)
logging不会抛出错误,可以输出到文件,还可以允许指定记录信息的级别,有debug, info, warning, error等几个级别,最后统一控制输出哪个级别的信息。
第四种方法:pdb 单步运行
让程序以单步运行的方式运行,可以设置断点,可以用IDE设置断点更为方便。
单元测试,unittest
单元测试用来对一个模块,函数或者类来进行正确性检验
打比方,有一个函数abs()
,我们可以有一个测试用例:输入正数如1, 0.5之类的,期待返回值与输入值相同。
例子: mydict.py
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
然后我们编写单元测试:mydict_test.py
import unittest
from mydict import Dict
class TestDict(unittest.TestCase): # 编写一个测试类,从unittest.TestCase继承
def test_init(self): # 以test开头的就是测试方法,不然就不是且不会被执行
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): # 另一种重要的断言就是期待抛出指定类型的error
value = d['empty'] # 这里通过访问不存在的key时,断言会抛出KeyError
def test_attrerror(self):
d = Dict()
with self.assertRaises(AttributeError):
value = d.empty
这里最常用的断言就是assertEqual()
:
self.assertEqual(abs(-1), 1)
运行单元测试:
推荐使用在命令行通过参数-m unittest
直接运行单元测试:
$ python -m unittest mydict_test
setUp & tearDown:
可以在单元测试中编写两个特殊的方法:setUp()
和 tearDown
方法,这两个方法会在每调用一个测试方法的前后分别被执行,所以前者可以用来连接数据库,后者用来关闭数据库。