7.错误处理和调试

错误捕获
调试
单元测试
文档测试

1.错误捕获
程序运行时,很容易出现错误,为了增强系统的健壮性,错误处理是少不了的,例如下面这块代码一但运行,程序就会因出错而自动结束了

def f():
r = 10 / 0
print('result:', r)
f()
输出结果:
Traceback (most recent call last):
  File "E:/python/project/test7/test7_1.py", line 15, in <module>
    f()
  File "E:/python/project/test7/test7_1.py", line 2, in f
    r = 10 / 0
ZeroDivisionError: division by zero

但是在实际应用中,某些时候出错是被需求考虑在内的,例如一个新闻类app在浏览一篇新闻时,先去本地文件里查找有没有这篇文章的缓存,如果有就直接加载缓存,否则去网络下载,下载后先把这篇文章缓存到本地文件,下次再点进来时直接去文件里加载这篇文章,这样可以节省用户流量,不必每次都去网络下载。其中,查找本地文件缓存时,就可能出现FileNotFoundException,类似这种异常如果不捕获处理,程序就会直接退出,后面的去网络加载的逻辑也执行不了了。

Python中的捕获异常写法:

def f1():
    try:
        r = 10 / 0
        print('result:', r)
    except ZeroDivisionError as e:
        print('except:', e)
    finally:
        print('finally...')
    print('END')
f1()

输出结果:
except: division by zero
finally...
END

except就相当于java的catch,一个try里可以有多个except,不过要注意,所有的Exception都是class,并继承自BaseException,会捕获其自身及其子类异常,所以要把范围小的写在上面,否则下面的永远也捕获不到异常了,有一个和java不一样的地方是,Python里的except后面可以接else

def f1():
    try:
        r = 10 / 1
        print('result:', r)
    except ZeroDivisionError as e:
        print('except:', e)
    else:
        print('no error')
    finally:
        print('finally...')
    print('END')


f1()
输出结果:
result: 10.0
no error
finally...
END

try…except可以跨跃多层调用:

def f():
    r = 10 / 0
    print('result:', r)


def f2():
    try:
        f()
    except Exception as e:
        print('except:', e)


f2()

输出结果:
except: division by zero

f方法里的错误未捕获,在f2方法里try…except也会捕获到。

自己抛出错误:

# 自定义一个error类型
class VError(ValueError):
    pass


def f3(i):
    if i == 0:
        raise VError('Invalid value:%d' % i)
    return 10 / i


f3(0)

另一种错误处理方式:

def f4():
    try:
        r = 10 / 0
        print('result:', r)
    except Exception as e:
        print('f4', e)
        raise
    finally:
        print('finally')
    print('end')


def f5():
    try:
        f4()
    except Exception as e:
        print('f5', e)


f5()
输出结果:
f4 division by zero
finally
f5 division by zero

在f4方法的第七行,打印错误后又把错误抛出了,如果别的方法调用f4,就要捕获这个抛出的错误,否则就会出异常,就算捕获了错误,第10行的end代码也不会执行了。

raise语句如果不带参数就会把错误原样抛出,另外,raise可以把一个错误转化成另一个类型抛出:

def f4():
    try:
        r = 10 / 0
        print('result:', r)
    except Exception as e:
        print('f4', e)
        raise VError('000 error!')
    finally:
        print('finally')
    print('end')

    def f5():
    try:
        f4()
    except Exception as e:
        print('f5', e)


f5()
输出结果:
f4 division by zero
finally
f5 000 error!

2.调试

import logging

logging.basicConfig(level=logging.DEBUG)  # 设置打印的Log级别

logging.debug('n = %d' % 1)  #输出Log

输出结果:
DEBUG:root:n = 1

断点调试使用IntelliJ IDEA和java调试方式一样。

3.单元测试

import unittest

from test7.test7_3 import Dict


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_keyerror(self):
        d = Dict()
        with self.assertRaises(KeyError):  # 调用d['key']期待抛出KeyError异常,
            value = d['key']

    def test_attrerror(self):
        d = Dict()
        with self.assertRaises(AttributeError):  # 调用d['key']期待抛出AttributeError异常,
            value = d.key


if __name__ == '__main__':  # 运行该.py文件里所有的test
    unittest.main()

可以通过在某个方法名上右键debug,这样会只debug这一个方法,也可以通过加上24和25两行代码,然后运行当前.py文件,一次运行所有的测试。

setUp和tearDown

class TestDict(unittest.TestCase):
    def setUp(self):
        print('before...')

    def tearDown(self):
        print('after...')

在上面的类里加上这两个方法后的运行结果是

before...
after...

before...
after...

before...
after...

会在每个方法运行之前执行before,在运行之后执行after,例如这些方法里测试数据库数据的话,就需要先连接数据库,测试完后要释放数据库连接,那么连接和断开连接就可以分别在setUp和tearDown方法里做。

4.文档测试
当编写注释时,可以定成这样:

def abs(n):
    '''
    Function to get absolute value of number.

    Example:

    >>> abs(1)
    1
    >>> abs(-1)
    1
    >>> abs(0)
    0
    '''
    return n if n >= 0 else (-n/0)

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

输出结果:
**********************************************************************
File "E:/python/project/test7/test7_4.py", line 9, in __main__.abs
Failed example:
    abs(-1)
Exception raised:
    Traceback (most recent call last):
      File "C:\Users\Administrator\AppData\Local\Programs\Python\Python35\lib\doctest.py", line 1321, in __run
        compileflags, 1), test.globs)
      File "<doctest __main__.abs[1]>", line 1, in <module>
        abs(-1)
      File "E:/python/project/test7/test7_4.py", line 14, in abs
        return n if n >= 0 else (-n/0)
    ZeroDivisionError: division by zero
**********************************************************************
1 items had failures:
   1 of   3 in __main__.abs
***Test Failed*** 1 failures.

注释中的这些示例代码可以在Python交互环境中执行,并输出注释中的结果,如果结果和文档中显示的不一样,说明调用有错,通过doctest.testmod()来开启文档测试。
利用这种方式测试前面的Dict类:

class Dict(dict):
    '''
    Simple dict but also support access as x.y style.

    >>> d1 = Dict()
    >>> d1['x'] = 100
    >>> d1.x
    100
    >>> d1.y = 200
    >>> d1['y']
    200
    >>> d2 = Dict(a=1, b=2, c='3')
    >>> d2.c
    '3'
    >>> d2['empty']
    Traceback (most recent call last):
        ...
    KeyError: 'empty'
    >>> d2.empty
    Traceback (most recent call last):
        ...
    AttributeError: 'Dict' object has no attribute 'empty'
    '''

    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


if __name__ == '__main__':
    import doctest

    doctest.testmod()

运行后没有任何结果,因为没有错误,代码都是按注释中的示例规则来执行和输出的,这时如果把setattr()方法去掉的话,第9行的设置就不会生效,第10行就不会按照文档要求走了,此时会输出这样的错误:

**********************************************************************
File "E:/python/project/test7/test7_4.py", line 26, in __main__.Dict
Failed example:
    d1['y']
Exception raised:
    Traceback (most recent call last):
      File "C:\Users\Administrator\AppData\Local\Programs\Python\Python35\lib\doctest.py", line 1321, in __run
        compileflags, 1), test.globs)
      File "<doctest __main__.Dict[4]>", line 1, in <module>
        d1['y']
    KeyError: 'y'
**********************************************************************
1 items had failures:
   1 of   9 in __main__.Dict
***Test Failed*** 1 failures.

猜你喜欢

转载自blog.csdn.net/aislli/article/details/81163544