27.python的错误(二)—调试、测试

1)调试

程序写完后不可避免的有bug,我们需要不断的调试bug以达到程序的完美,那么我们需要一整套的调试程序的手段来修复bug。

(1)print

我们可以直接打印出可能有错误的变量。示例如下:

def fun(s):
    n=int(s)
    print('n=',n)
    return 10/n
if __name__ == '__main__':
   fun('0')

输出结果如下:

(2)assert

assert可以用来代替print。示例代码如下:

def fun(s):
    n=int(s)
    assert n!=0,'n is 0'
    return 10/n
if __name__ == '__main__':
   fun('0')

这里assert的意思是n!=0应该为真,否则就出错。如果assert为假,那么就抛出错误。输出结果如下:

2)测试

(1)单元测试

什么是单元测试呢?比如说我们完成了一个函数abs()。我们编写如下测试程序段:

  • 在输入1,2,3的时候,返回1,2,3。

  • 输入-1,-2,-3的时候,返回1,2,3。

  • 输入0的时候,返回0。

  • 输入非数值类型,返回TypeError。

将以上的程序段放到一个模块里,利用该模块不断试验完善程序的过程叫做单元测试

如果单元测试通过,说明我们测试的这个函数能够正常工作。如果不正确那么需要调试。那么单元测试有什么意义呢?我们在每次对原函数(abs)修改后,可以用单元测试来确保原函数的功能没有被破坏。

举个例子:我们来编写一个Dict类,这个类的行为和dict一致,但是可以通过属性来访问,如下效果:

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

输出结果如下:

接下来,我们编写单元测试模块:

这里我们需要引入python自带的unittest模块,编写test.py如下:

import unittest
from exercise 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.keys,'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(AttributeError):
            value=d.empty
    def test_atterror(self):
        d=Dict()
        with self.assertRaises(AttributeError):
            value=d.empty
if __name__ == '__main__':
    unittest.main()

输出结果如下:

这里,我们编写了5个测试程序。其中通过了4个,有一个失败了。TestDict类中以test开头的就是测试程序,不是test开头的就不是测试方法,测试的时候不会被执行。对每一类测试都需要编写一个test_xxx()方法。由于unittest.TestCase提供了很多内置的条件判断,我们只需要调用这些方法就可以断言输出是否是我们所期望的。最常用的断言就是assertEqual():

self.assertEqual(abs(-1), 1) # 断言函数返回的结果与1相等

另一种重要的断言就是期待抛出指定类型的Error,比如通过d['empty']访问不存在的Key时,断言会抛出KeyError:

with self.assertRaises(KeyError):
    value = d['empty']

而通过d['empty']访问不存在的Key时,我们期待抛出AttributeError:

with self.assertRaises(AttributeError):
    value = d.empty

(2)文件测试

在Python的官方文档中,很多文档都有示例代码。如re模块就带了很多示例代码:

>>> import re
>>> m=re.search('(?<=abc)def','abcdef')
>>> m.group(0)
'def'

可以把这些示例代码在python的交互式环境下输入并执行,结果与文档中的示例代码显示的一致。这些代码与其他说明可以写在注释中,然后由一些工具来自动生成文档。既然这些代码本身可以粘贴出来直接运行,那么我们不如直接直接运行注释中的代码,何必再粘贴呢?问题是这样可以吗?可以的话,又得怎么做呢?方法如下:

我们在编写注释的时候,写上这样的注释:

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)

那么程序员将更加理解这个函数的用法。并且,python内置的"文档测试"(doctest)模块可以直接提取注释中的代码并执行测试。

doctest严格按照python交互式命令行输入和输出来判断测试结果是否正确。只有测试异常的时候,可以用...来表示中间的错误提示输出。

PS:

这里,我们最后用了if __name__='__main__'结构。我们这里补充介绍一下:

一个python.py文件可以有两种使用方法,一种是作为脚本直接执行,另一种就是Import到其他的python脚本中被调用(模块重用)执行。而这段代码的作用就是来区分这两种使用方法:只有在第一种情况下,其冒号后面的代码才会执行。在第二种情况下,当该模块被import到其他脚本下,其冒号后面的代码是不会执行。这里举个例子:

我们在exercise.py中输入以下代码:

print("first")
if __name__=="__main__":
    print("second")

运行该exercise.py,结果如下:

在test.py中输入以下代码:

import exercise

结果如下:

这样结果就很明显了,第一个程序if __name__='__main__'前后面的代码都执行了,第二个程序只执行了前面的代码

希望有志同道合的小伙伴关注我的公众平台,欢迎您的批评指正,共同交流进步。


阅读更多
个人分类: python
所属专栏: python学习
上一篇10.神经网络与深度学习(九)—梯度消失问题
下一篇11.神经网络与机器学习(十)—卷积神经网络(CNN)
想对作者说点什么? 我来说一句

调试测试和异常处理

2008年11月21日 2.12MB 下载

没有更多推荐了,返回首页

关闭
关闭