错误、调试和测试——Python学习笔记09

来源:互联网 发布:淘宝用别人信用卡支付 编辑:程序博客网 时间:2024/06/06 05:55

错误处理

try

def foo():                    #先定义两个函数    r = some_function()    if r == (-1):        return (-1)    return rdef bar():    r = foo()    if r == (-1):        print('Error')    else:        pass

try的机制

try:    print('try…')    r = 10 / 'a'    print('result:', r)except ValueError as e:      #错误类型有很多,如果有不同的类型错误,应该由不同的except语句块处理    print('ValueError:', e)except ZeroDivisionError as e:    print('ZeroDivisionError:', e)    #所有错误的类型都继承自BaseException,不但捕获该类型的错误,还把其子类一网打尽else:    print('no Error!')    #finally:    print('finally…')            #可以没有finally语句print('END')try还可以跨越多层调用:def foo(s):    return 10 / int(s)def bar(s):    return foo(s) * 2def main():    try:        bar('0')    except Exception as e:        print('Error', e)    finally:        print('finally...')

调用堆栈

编写一个错误范例:

#err.pydef foo(s):    return 10 / int(s)def bar(s):    return foo(s) * 2def main():    bar('0')main()执行结果:Traceback (most recent call last):    #tell us this is 错误的跟踪信息  File "err.py", line 11, in <module>    #调用main()出错了,在第11行代码,但是真正的原因是在第9行    main()  File "err.py", line 9, in main    #调用的bar(‘0’)出错了,但是原因在第6行    bar('0')  File "err.py", line 6, in bar    #return foo(s) * 2这个语句出错了,但是原因在第3行    return foo(s) * 2  File "err.py", line 3, in foo    #原来是return 10 / int(s)出错了,下方打印的是错误产生的源头    return 10 / int(s)ZeroDivisionError: division by zero    #根据错误类型,我们判断int(s)本身没有出错,但是返回0,在计算10 / 0时出错

记录错误

logging可以记录错误,并且可以让程序打印完错误信息后继续运行:

#error_loggingimport logging    #导入logging模块def foo(s):    return 10 / int(s)def bar(s):    return foo(s) * 2def main():    try:        bar('0')    except Exception as e:        logging.except(e)main()print('END')

运行结果:

ERROR:root:division by zeroTraceback (most recent call last):    #打印错误信息  File "/Users/Mojian/Desktop/learnpy/error_logging.py", line 12, in main    bar('0')  File "/Users/Mojian/Desktop/learnpy/error_logging.py", line 8, in bar    return foo(s) * 2  File "/Users/Mojian/Desktop/learnpy/error_logging.py", line 5, in foo    return 10 / int(s)ZeroDivisionError: division by zeroEND    #程序继续运行,并正常退出

抛出错误

用raise语句跑出一个错误的实例:

class FooError(ValueError):    passdef foo(s):    n = int(s)    if n == 0:        raise FooError('invalid value: %s' % s)        #尽量使用Python内置的错误类型,如ValueError等    return 10 / nfoo('0')

执行后,可以跟踪我们定义的错误:

Traceback (most recent call last):  File "/Users/Mojian/Desktop/learnpy/error_raise.py", line 11, in <module>    foo('0')  File "/Users/Mojian/Desktop/learnpy/error_raise.py", line 8, in foo    raise FooError('invalid value: %s' % s)FooError: invalid value: 0

还有另外一种错误处理方式:

def foo(s):    n = int(s)    if n == 0:        raise ValueError('invalid value: %s' % s)    return 10 / ndef bar():    try:        foo('0')    except ValueError as e:        print('ValueError:', e)        raise                    #raise如果不加参数,会把错误原样抛出bar()#返回:ValueError: invalid value: 0  File "<input>", line 13, in <module>  File "<input>", line 9, in bar  File "<input>", line 4, in fooValueError: invalid value: 0

运行过程:

1.bar()调用,函数开始执行,执行try—>foo('0') —>调用foo函数,参数’0’2.foo('0')开始执行,抛出错误raise ValueError('invalid value: %s' % 0)3.foo('0')此时抛出的错误不会在窗口中打印,抛到它的上级调用者----bar()中的try中4.try…except捕获到抛出的错误,except对错误类型进行对比—>找到此类型的错误处理信息—>错误进行处理—>错误再次抛出—>抛出到上级5.上级无try...except捕获并处理错误的语句,错误信息在窗口中打印出来

在except中raise一个Error,还可以把一种类型的错误转化成另一种,但,决不能把一个IOError 转换成 不相干的ValueError

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

调试

print
调试第一种方法是直接打印出来调试信息:

def foo(s):    n = int(s)    print('>>> n = %s' % n)    return 10 / ndef main():    foo('0')main()

执行后在输出中查找打印的变量值:

>>> n = 0Traceback (most recent call last):  File "/Users/Mojian/Documents/调试1.py", line 9, in <module>    main()  File "/Users/Mojian/Documents/调试1.py", line 7, in main    foo('0')  File "/Users/Mojian/Documents/调试1.py", line 4, in foo    return 10 / nZeroDivisionError: division by zero

坏处是还需删除print()

断言

def foo(s):    n = int(s)    assert n != 0, 'n is zero!'#判断n 是否为0,为0,抛出AssertionError    return 10 / n    #assert为Ture,则 继续进行def main():        #调用foo()    foo('0')

logging

import logginglogging.basicConfig(level = logging.INFO)#指定记录信息的级别(debug,info,warningerror等),当指定level=warning时,logging.debug,info就不起作用s = '0'n = int(s)logging.info('n is %d' %n)print(10 / n)

单元测试

编写一个Dict类,这个类的行为和dict一致,但是可以通过属性访问,mydict.py:

class Dict(dict):    def __init__(self, **kw):      #__init__是构造函数(附带任何可选参数)        super().__init__(**kw)     #super继承父类,调用父类的__init__()    def __getattr__(self, key):    #__getattr__获得属性(getattr()内置方法)        try:            return self[key]        except KeyError:            raise AttributeError(r"'Dict has no attribute '%s'' % key)    def __setattr__(self, key, value):        self[key] = value    #__setattr__设置属性;setattr赋给self对象的key属性 值 value

编写mydict_test:

import unittest#需引入Python自带的unitest模块from mydict import Dictclass TestDict(unitest.TestCase):#编写一个单元测试,需编写一个测试类,需从unittest.TestCase继承    def setUp(self):#可以在单元test中增加两个特殊方法,这两方法会分别在每调用一个测试方法的前后分别被执行        print('setUp…')    def tearDown(self):#可以在setUp()中连接数据库,在tearDown中关闭数据库,不必在每个测试中重复代码        print('tearDown…')    def test_init(self):    #以test开头的方法是测试方法,才会在单元测试中被测试        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’)#最常用的断言assertEqual(),断言两个值相等    def test_attr(self):        d = Dict()        d.key = 'value'        self.assertTrue('key' in d)        #断言d中含有'key'属性        self.assertEqual(d['key'], value)    def test_keyerror(self):        d = Dict()        with self.assertRaises(Keyerror):#另一种断言,期待抛出指定类型的Error            value = d['empty’]    def test_attrerror(self):        d = Dict()        with self.assertRaises(AttributeError):#如果value=d.empty,抛出的错误为AttributeError,则断言成功            value = d.empty    if __name__ == '__main__':        unittest.main()            #所有的模块都有一个内置属性 __name__#当我们在命令行运行该模块文件时,Python解释器把一个特殊变量__name__置为__main__,而如果在其他地方导入该模块时,if判断将失败,因此,这种if测试可以让一个模块通过命令行运行时执行一些额外的代码,最常见的就是运行测试。

文档测试

# -*- coding: utf-8 -*-def fact(n):    '''    >>> fact(3)    6    >>> fact(1)    1    >>> fact(0)    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                    #Python内置的doctest模块可以直接提取注释中的代码并执行测试    doctest.testmod()                 #运行后什么输出也没有#如果出现错误,程序就会报错,如:**********************************************************************File "mydict3.py", line 9, in __main__.factFailed example:    fact(0)Exception raised:    Traceback (most recent call last):      File "/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/doctest.py", line 1321, in __run        compileflags, 1), test.globs)      File "<doctest __main__.fact[2]>", line 1, in <module>        fact(0)      File "mydict3.py", line 15, in fact        raise ValueError    ValueError**********************************************************************1 items had failures:                #表示测试执行失败数,无失败时无输出   1 of   3 in __main__.fact***Test Failed*** 1 failures.
原创粉丝点击