python 指南(09)错误、调试、测试

错误处理

一、try–except–finally

  • 基本的错误的处理格式如下
try:
    print('try...')
    r = 10 / int('2')
    print('result:', r)
except ValueError as e:
    print('ValueError:', e)
except ZeroDivisionError as e:
    print('ZeroDivisionError:', e)
else:
    print('no error!')
finally:
    print('finally...')
print('END')
  • 在except语句块后面加一个else,当没有错误发生时,会自动执行else语句
  • 可以用多个 except 捕获不同类型的错误,但是必须记住一点,有继承关系的错误类型一定是子类在前,父类在后,不然的话,错误会被父类全部给捕获了
  • 对于方法的层层调用,我们不必在每一层对可能出现的错误都进行处理,只要在适当的层次用try语句块进行处理即可,底层发生的错误会层层往上抛直到被处理
  • 出错的时候,一定要分析错误的调用栈信息,才能定位错误的位置

二、错误记录

  • 就是用 logging 进行错误日志的输出和记录
import logging

try:
    ........
except Exception as e:
    logging.exception(e)

三、抛出错误

  • 可以继承python内置的错误的类型,自定义自己的错误类型,但是能用现成的还是用现成的
  • 我们自己定义的方法也可以抛出错误
  • 将错误捕获之后只是记录一下错误的信息,再次抛出是一种常见的处理方法,尤其是当前的方法不知道怎么处理该错误的时候
  • 错误的抛出既可以直接raise原样抛出,也可以适当转换再抛出,但并步表示是随意转换哟
try:
    .......
except ValueError as e:
    print('ValueError!')
    raise


try:
    10 / 0
except ZeroDivisionError:
    raise ValueError('input error!')

程序调试

  • python程序调试最简单的就是使用print直接打印变量的值

一、断言

  • 实际的使用效果比print也好不到哪里
def foo(s):
    n = int(s)
    assert n != 0, 'n is zero!'
    return 10 / n

def main():
    foo('0')
  • python运行取消断言:python -O err.py

二、logging

  • 作用就是记录程序的运行信息,不会抛出错误
  • 可以指定运行信息输出的位置(控制台和文件等)
  • logging的四个记录级别:debug、info、warning、error
  • 使用logging先要设置日志的级别,大于等于该级别的日志会被输出
import logging
logging.basicConfig(level=logging.INFO)

s = '0'
n = int(s)
logging.info('n = %d' % n)
print(10 / n)

三、pdb

  • pdb就是python为我们提供的调试工具
  • pdb我是没用过,感觉不是很方便,这里直接跳过

四、IDE

  • 日常程序的开发多是在IDE中进行,所以使用IDE的调试功能来调试程序是很常见的了

单元测试

  • 单元测试是用来对一个模块、一个函数或者一个类来进行正确性检验的测试工作
  • 这种以测试为驱动的开发模式最大的好处就是确保一个程序模块的行为符合我们设计的测试用例
  • 在将来修改的时候只要能通过单元测试就可以极大程度地保证该模块行为仍然是正确的
  • 单元测试的测试用例要覆盖常用的 输入组合边界条件异常
  • 单元测试代码要非常简单,如果测试代码太复杂,那么测试代码本身就可能有bug
  • 单元测试通过了并不意味着程序就没有bug了,但是不通过程序肯定有bug

一、具体测试说明

  • 单元测试依赖于unittest模块,需要先导入
  • 编写一个测试类,从unittest.TestCase继承
  • 以test开头的方法就是测试方法,不以test开头的方法不被认为是测试方法不会被执行
  • unittest.TestCase提供了很多内置的条件判断(assertEqual等),只需要调用这些方法就可以断言输出是否是期望值
  • 可以在单元测试中编写两个特殊的setUp()和tearDown()方法,这两个方法会分别在每调用一个测试方法的前后分别被执行和java单元测试的before和after是一个效果
import unittest


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


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


if __name__ == '__main__':
    unittest.main()

二、with语句说明

1、基本用法

  • with语句本身可以看成是try–finally语句的简写,但是实际上并不能捕获处理错误,无论是否发生错误资源都能够正确被关闭
  • with后面的open方法是一个context表达式,表达式返回的对象保存在后面的变量f里面
with open('1.txt') as f:
    print(f.read())

2、with的本质:上下文管理

  • 上下文管理协议:包含方法__enter__()__exit__(),支持该协议对象要实现这两个方法
  • with语句有一个上下文管理器,负责执行with语句块上下文对象中的进入与退出操作
  • 进入上下文的时候执行__enter__方法,如果设置as var语句,var变量接受__enter__()方法返回值
  • 如果运行时发生了异常,就退出上下文管理器,调用管理器__exit__方法

3、自定义类使用with

class Myclass(object):
    def __init__(self):
        pass

    def __enter__(self):
        print('enter....')

    def __exit__(self, exc_type, exc_val, exc_tb):
        print('exit...')
        print(exc_type)
        print(exc_val)
        print( exc_tb)


if __name__ == '__main__':
    with Myclass() as my:
        i = 10/1
  • 正常执行输出
    在这里插入图片描述
  • 发生异常输出:将里面的10/1改为10/0
    在这里插入图片描述

文档测试

  • 就是函数文档注释里面写的测试用例,基本的书写的格式如下所示
  • Python内置的文档测试(doctest)模块可以直接提取注释中的代码并执行测试
  • doctest严格按照Python交互式命令行的输入和输出来判断测试结果是否正确
  • 只有测试异常的时候,可以用...表示中间一大段烦人的方法栈信息的输出
  • 当模块正常导入时doctest不会被执行,只有直接运行时才执行doctest,不必担心doctest会在非测试环境下执行
def fact(n):
    '''
    Calculate 1*2*...*n
    
    >>> fact(1)
    1
    >>> fact(10)
    3628800
    >>> fact(-1)
    Traceback (most recent call last):
        ...
    ValueError
    '''
    if n < 1:
        raise ValueError()
    if n == 1:
        return 1
    return n * fact(n - 1)

if __name__=='__main__':
    import doctest
    doctest.testmod()

发布了153 篇原创文章 · 获赞 51 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/stanwuc/article/details/103684909