欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Python3 错误、调试和测试

程序员文章站 2022-04-15 21:58:22
1.错误处理 try: print('try...') r = 10 / int('2') print('result:', r) except ValueError as e: print('ValueError:', e) except ZeroDivisionError: ##最后一个exce ......

1.错误处理

Python3 错误、调试和测试

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已有的内置的错误类型(比如valueerrortypeerror),尽量使用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 的子类)。

  在exceptraise一个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)

  它允许你指定记录信息的级别,有debuginfowarningerror等几个级别,当我们指定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和菜鸟