python第11课 异常

1.try

try:
    print('try...')
    r = 10 / int('a')
    print('result:', r)
except ValueError as e:
    print('ValueError:', e)
except ZeroDivisionError as e:
    print('ZeroDivisionError:', e)
finally:
    print('finally...')
print('END')

Python的错误其实也是class,所有的错误类型都继承自BaseException
,所以在使用except时需要注意的是,它不但捕获该类型的错误,还把其子类也“一网打尽”。比如:

try:
    foo()
except ValueError as e:
    print('ValueError')
except UnicodeError as e:
    print('UnicodeError')

第二个except永远也捕获不到UnicodeError,因为UnicodeError
是ValueError的子类,如果有,也被第一个except给捕获了。
抛出错误

# 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')

最后,我们来看另一种错误处理的方式:

# 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语句如果不带参数,就会把当前错误原样抛出。

2.调试logging

import logging
logging.basicConfig(level=logging.INFO)

logging的好处,它允许你指定记录信息的级别,有debug,info,warning,error等几个级别,当我们指定level=INFO时,logging.debug就不起作用了。同理,指定level=WARNING后,debug和info就不起作用了。这样一来,你可以放心地输出不同级别的信息,也不用删除,最后统一控制输出哪个级别的信息。

3.单元测试

我们来编写一个Dict类,这个类的行为和dict一致,但是可以通过属性来访问,用起来就像下面这样:

>> d = Dict(a=1, b=2)
>>> d['a']
1
>>> d.a
1
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

为了编写单元测试,我们需要引入Python自带的unittest模块,编写mydict_test.py如下:

import unittest

from mydict 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_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

以test开头的方法就是测试方法,不以test开头的方法不被认为是测试方法,测试的时候不会被执行。

运行单元测试

最简单的运行方式是在mydict_test.py的最后加上两行代码:

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

这样就可以把mydict_test.py当做正常的python脚本运行:

$ python3 mydict_test.py

另一种方法是在命令行通过参数-m unittest直接运行单元测试

$ python3 -m unittest mydict_test
.....
----------------------------------------------------------------------
Ran 5 tests in 0.000s

OK

这是推荐的做法,因为这样可以一次批量运行很多单元测试,并且,有很多工具可以自动来运行这些单元测试
setUp与tearDown

class TestDict(unittest.TestCase):

    def setUp(self):
        print('setUp...')

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

你可能感兴趣的:(python第11课 异常)