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

错误处理

try

def foo():                    #先定义两个函数
    r = some_function()
    if r == (-1):
        return (-1)
    return r

def 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) * 2

def main():
    try:
        bar('0')
    except Exception as e:
        print('Error', e)
    finally:
        print('finally...')

调用堆栈

编写一个错误范例:

#err.py
def foo(s):
    return 10 / int(s)

def bar(s):
    return foo(s) * 2

def 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_logging
import logging    #导入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.except(e)
main()
print('END')

运行结果:

ERROR:root:division by zero
Traceback (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 zero
END    #程序继续运行,并正常退出

抛出错误

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

class FooError(ValueError):
    pass

def foo(s):
    n = int(s)
    if n == 0:
        raise FooError('invalid value: %s' % s)        #尽量使用Python内置的错误类型,如ValueError等
    return 10 / n
foo('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 / n

def 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 foo
ValueError: 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 / 0
except ZeroDivisionError as e:
    raise ValueError('input error!')

调试

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

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

def main():
    foo('0')

main()

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

>>> n = 0
Traceback (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 / n
ZeroDivisionError: 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 logging

logging.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 Dict

class 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__.fact
Failed 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.
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值