八、错误、调试与测试

1、错误处理

  • try...except...(finally...):finally可不写,可加‘else’表在没有错误时执行else下的内容
     1 try:
     2     r = 10 / 0
     3     print('result:',r)
     4 except  ZeroDivisionError as e:
     5     print('Error:',e)
     6 # except ZeroDivisionError:
     7 #     print('除数不能为零。')
     8 finally:
     9     print('End')
    10 
    11 输出:
    12 Error: division by zero
    13 #除数不能为零。
    14 End
  • 调用栈:没有捕捉错误时,需分析错误的调用栈信息,才能定位错误的位置(即打印时报错的内容)
  • 记录错误(logging):能记录错误信息,能把错误堆栈打印出来并继续执行程序。(可用于将错误信息记录在日志中)
     1 import logging
     2 def foo(s):
     3     return 10 / int(s)
     4 def bar(s):
     5     return foo(s) * 2
     6 def main():
     7     try:
     8         bar('0')
     9     except Exception as e:
    10         logging.exception(e)
    11 
    12 main()
    13 print('END')
    14 
    15 输出:
    16 END
    17 ERROR:root:division by zero
    18 Traceback (most recent call last):
    19   File "D:/Pycharm...(略)
    20     ...line 469, in foo
    21     return 10 / int(s)
    22 ZeroDivisionError: division by zero
  • 抛出错误(raise):根据需要,定义一个错误的class,选择好继承关系,然后用raise语句抛出一个错误的实例
     1 #定义一个错误的类
     2 class FooError(ValueError):
     3     pass
     4 def foo(s):
     5     n = int(s)
     6     if n == 0:
     7         # 追踪到自己定义的错误信息
     8         raise FooError('invalid value:%s' %s)
     9     return 10 / 0
    10 foo('0')
    11 
    12 输出:
    13 Traceback (most recent call last):
    14   File "D:/Pycharm/廖雪峰课程/函数.py", line 460, in <module>
    15     foo('0')
    16   File "D:/Pycharm/廖雪峰课程/函数.py", line 458, in foo
    17     raise FooError('invalid value:%s' %s)
    18 __main__.FooError: invalid value:0    #raise的内容
  • 调试:
  1. assert(断言):表达式应该为True,断言错误抛出AssertError错误(启动Python解释器时可以用-O参数来关闭assert
     1 def func(s):
     2     n = int(s)
     3     assert n != 0,'n is zero!'  
     4     return 10 / n
     5 def main():
     6     func('0')
     7 main()
     8 
     9 输出:
    10 Traceback (most recent call last):
    11   File "D:/Pycharm...
    12     ...(略)
    13     assert n != 0,'n is zero!'
    14 AssertionError: n is zero!
  2. logging:不会抛出错误,可指定记录信息的级别,有debuginfowarningerror等几个级别
     1 import logging
     2 logging.basicConfig(level=logging.INFO)
     3 s = '0'
     4 n = int(s)
     5 logging.info('n = %d'%n)
     6 print(10/n)
     7 
     8 输出:
     9 INFO:root:n = 0
    10 Traceback (most recent call last):
    11   File "D:/...(略)
    12     print(10/n)
    13 ZeroDivisionError: division by zero
  3. pdb:调试器pdb,让程序以单步方式运行,可以随时查看运行状态。以参数-m pdb启动后,命令: l 查看代码;n 单步执行代码;p + 变量名 查看变量;q 结束调试。
  4. pdb.set_trace():放在可能出错的地方,设置一个断点,不需要单步执行,命令 c 继续运行。
     1 import pdb
     2 s = '0'
     3 n = int(s)
     4 pdb.set_trace()
     5 print(10/n)
     6 
     7 输出:
     8 > d:pycharm...<module>()
     9 -> print(10/n)
    10 (Pdb) p n
    11 0
    12 (Pdb) q
  • 单元测试:使用了assertEqual断言与错误处理。(setUp与tearDown之间的内容会在每段测试前后被执行一遍,如测试需要启动一个数据库,可在setUp()方法中连接数据库,在tearDown()方法中关闭数据库,这样做可不必在每个测试方法中重复相同的代码)
     1 #func.py文件
     2 class Dict(dict):
     3     def __init__(self,**kw):
     4         super().__init__(**kw)  #调用父类的init方法
     5     def __getattr__(self, key): #设置返回属性值key
     6         try:
     7             return self[key]
     8         except KeyError:
     9             raise AttributeError(r"'Dict' object has no attribute '%s'" % key)
    10     def __setattr__(self,key,value):
    11         self[key] = value
     1 #用于测试的类(test.py)
     2 import unittest
     3 from func import Dict
     4 class Testdict(unittest.TestCase):  #测试类需继承unittest.TestCase
     5     # def setUp(self):
     6     #     print('setup...')
     7     def test_init(self):
     8         d = Dict(a = 1,b = 'test')
     9         self.assertEqual(d.a,1) #断言d对象中a属性的值是否为1
    10         self.assertEqual(d.b,'test')
    11         self.assertTrue(isinstance(d,dict))
    12 
    13     def test_key(self):     #测试key
    14         d = Dict()
    15         d['key'] = 'value'
    16         self.assertEqual(d['key'], 'value')
    17 
    18     def test_attr(self):    #测试属性
    19         d = Dict()
    20         d.key = 'value'
    21         self.assertTrue('key' in d)
    22         self.assertEqual(d.key, 'value')
    23 
    24 
    25 # 通过d['empty']访问不存在的key时,抛出指定类型的Error
    26     def test_keyerror(self):    #抛出key错误
    27         d = Dict()
    28         with self.assertRaises(KeyError):
    29             value = d['empty']
    30 
    31     def test_attrerror(self):   #抛出属性错误
    32         d = Dict()
    33         with self.assertRaises(AttributeError):
    34             value = d.empty
    35 
    36     # def tearDown(self):
    37     #     print('tearDown...')
    38 
    39     if __name__ == '__main__':  #使该文件当做正常脚本运行
    40         unittest.main()
    41 
    42 
    43 输出:
    44 Testing started at 16:15 ...
    45 D:Py...(略)
    46 #setUp与tearDownd 内容
    47 Ran 5 tests in 0.002s
    48 
    49 OK
  • 文档测试:运行没内容输出时说明编写的doctest运行都是正确的。更明确地告诉函数的调用者该函数的期望输入和输出,内置的 doctest 模块可以直接提取注释中的代码并执行测试,测试异常的时候,可以用...表示中间内容的输出。
     1 class Dict(dict):
     2     '''
     3     Simple dict but also support access as x.y style.
     4 
     5     >>> d1 = Dict()
     6     >>> d1['x'] = 100
     7     >>> d1.x
     8     100
     9     >>> d1.y = 200
    10     >>> d1['y']
    11     200
    12     >>> d2 = Dict(a=1, b=2, c='3')
    13     >>> d2.c
    14     '3'
    15     >>> d2['empty']
    16     Traceback (most recent call last):
    17         ...
    18     KeyError: 'empty'
    19     >>> d2.empty
    20     Traceback (most recent call last):
    21         ...
    22     AttributeError: 'Dict' object has no attribute 'empty'
    23     '''
    24     def __init__(self, **kw):
    25         super(Dict, self).__init__(**kw)
    26 
    27     def __getattr__(self, key):
    28         try:
    29             return self[key]
    30         except KeyError:
    31             raise AttributeError(r"'Dict' object has no attribute '%s'" % key)
    32 
    33     def __setattr__(self, key, value):
    34         self[key] = value
    35 
    36 if __name__=='__main__':
    37     import doctest
    38     doctest.testmod()