Python3 错误、调试和测试
1.错误处理
try: print('try...') r = 10 / int('2') print('result:', r) except valueerror as e: print('valueerror:', e) except zerodivisionerror: ##最后一个except子句可以忽略异常的名称,它将被当作通配符使用 print('zerodivisionerror:', e) else: print('no error!') finally: print('finally...') print('end')
finally
一定会被执行(可以没有finally
语句)。
如果一个异常没有与任何的 excep 匹配,那么这个异常将会传递给上层的 try 中。
一个except子句可以同时处理多个异常,这些异常将被放在一个括号里成为一个元组,例如: except (runtimeerror, typeerror, nameerror): pass
如果没有错误发生,可以在except
语句块后面加一个else
,当没有错误发生时,会自动执行else
语句
python所有的错误都是从baseexception
类派生的,写错误类的时候,可能会其子类也“一网打尽”,注意范围。
常见的错误类型和继承关系看这里:
捕获错误,可以多层调用。
出错的时候,一定要分析错误的调用栈信息,才能定位错误的位置。
python内置的logging
模块可以非常容易地记录错误信息:
# err_logging.py 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')
同样是出错,但程序打印完错误信息后会继续执行,并正常退出。
如果要抛出错误,首先根据需要,可以定义一个错误的class,选择好继承关系,然后,用raise
语句抛出一个错误的实例:
# err_raise.py 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已有的内置的错误类型(比如valueerror
,typeerror
),尽量使用python内置的错误类型。
# err_reraise.py def foo(s): n = int(s) if n==0: raise valueerror('invalid value: %s' % s) return 10 / n def bar(): try: foo('0') except valueerror as e: print('valueerror!') raise bar()
raise
语句如果不带参数,就会把当前错误原样抛出。
raise 唯一的一个参数指定了要被抛出的异常。它必须是一个异常的实例或者是异常的类(也就是 exception 的子类)。
在except
中raise
一个error,还可以把一种类型的错误转化成另一种类型:只要是合理的转换逻辑就可以
try: 10 / 0 except zerodivisionerror: raise valueerror('input error!')
2.调试
1.用print()
把可能有问题的变量打印出来看看。
2.凡是用print()
来辅助查看的地方,都可以用断言(assert)来替代。
def foo(s): n = int(s) assert n != 0, 'n is zero!' return 10 / n def main(): foo('0')
assert
的意思是,表达式n != 0
应该是true
,否则,根据程序运行的逻辑,后面的代码肯定会出错。
如果断言失败,assert
语句本身就会抛出assertionerror
:
$ python err.py traceback (most recent call last): ... assertionerror: n is zero!
启动python解释器时可以用-o
参数来关闭assert。关闭后,你可以把所有的
assert
语句当成pass
来看。
3.logging
不会抛出错误,而且可以输出到文件。logging.info()
就可以输出一段文本
import logging logging.basicconfig(level=logging.info) s = '0' n = int(s) logging.info('n = %d' % n) print(10 / n)
它允许你指定记录信息的级别,有debug
,info
,warning
,error
等几个级别,当我们指定level=info
时,logging.debug
就不起作用了。
logging
的另一个好处是通过简单的配置,一条语句可以同时输出到不同的地方,比如console和文件。
4.启动python的调试器pdb。
import pdb
,然后,在可能出错的地方放一个pdb.set_trace()
,就可以设置一个断点:
# err.py import pdb s = '0' n = int(s) pdb.set_trace() # 运行到这里会自动暂停 print(10 / n)
运行代码,程序会自动在pdb.set_trace()
暂停并进入pdb调试环境,可以用命令p
查看变量,或者用命令c
继续运行
pycharm的调试程序:
3.单元测试
单元测试是用来对一个模块、一个函数或者一个类来进行正确性检验的测试工作。
需要引入python自带的unittest
模块.
用到再细查。大体是看懂了。一定要做单元测试。减少测试人员的工作量,也减少很多不必要的错误。
单元测试的测试用例要覆盖常用的输入组合、边界条件和异常。
# -*- coding: utf-8 -*- import unittest class student(object): def __init__(self, name, score): self.name = name self.score = score def get_grade(self): # if self.score < 0 or self.score > 100: # raise valueerror('valueerror') if self.score >= 60 and self.score < 80: return 'b' elif self.score >= 80 and self.score <= 100: return 'a' elif self.score >= 0 and self.score < 60: return 'c' else: raise valueerror("wrong key") class teststudent(unittest.testcase): def test_80_to_100(self): s1 = student('bart', 80) s2 = student('lisa', 100) self.assertequal(s1.get_grade(), 'a') self.assertequal(s2.get_grade(), 'a') def test_60_to_80(self): s1 = student('bart', 60) s2 = student('lisa', 79) self.assertequal(s1.get_grade(), 'b') self.assertequal(s2.get_grade(), 'b') def test_0_to_60(self): s1 = student('bart', 0) s2 = student('lisa', 59) self.assertequal(s1.get_grade(), 'c') self.assertequal(s2.get_grade(), 'c') def test_invalid(self): s1 = student('bart', -1) s2 = student('lisa', 101) with self.assertraises(valueerror): s1.get_grade() with self.assertraises(valueerror): s2.get_grade() if __name__ == '__main__': unittest.main()
4.文档测试
# -*- coding: utf-8 -*- def fact(n): ''' calculate 1*2*...*n >>> fact(1) 1 >>> fact(10) 3628800 >>> fact(-1) traceback (most recent call last): file "c:\python\lib\doctest.py", line 1329, in __run compileflags, 1), test.globs) file "<doctest __main__.fact[2]>", line 1, in <module> fact(-1) file "c:/workspace/document/private/code/python/tutorial/study.py", line 917, in fact raise valueerror() valueerror ''' if n < 1: raise valueerror() if n == 1: return 1 return n * fact(n - 1) if __name__ == '__main__': import doctest doctest.testmod()
python内置的“文档测试”(doctest)模块可以直接提取注释中的代码并执行测试。
doctest严格按照python交互式命令行的输入和输出来判断测试结果是否正确。只有测试异常的时候,可以用...
表示中间一大段烦人的输出。
当模块正常导入时,doctest不会被执行。只有在命令行直接运行时,才执行doctest。所以,不必担心doctest会在非测试环境下执行。
参考自:廖老师的python3和菜鸟
上一篇: vue proxy 跨域代理
下一篇: Go语言库系列之flag