python入门基础总结笔记(7)——错误、调试和测试

python入门基础总结笔记(7)——错误、调试和测试

学习采用的是廖雪峰老师的python教程,很精彩详细,收获很大,聊表感谢!原文链接:https://www.liaoxuefeng.com/wiki/1016959663602400

目录

  • 1.错误处理
  • 2.调试 (PyCharm)
  • 3.单元测试
  • 4.文档测试

1.错误处理

在Python中,如果程序运行出现错误,可以事先约定返回一个错误代码,这样,就可以知道是否有错,以及出错的原因。

一旦出错,还要一级一级上报,直到某个函数可以处理该错误(比如,给用户输出一个错误信息)。

高级语言通常都内置了一套try…except…finally…的错误处理机制,python也不例外。

1.1 try…except…finally…

try:
    print('try...')
    r = 10 / 0        #除法错误
    print('result:', r)
except ZeroDivisionError as e:
    print('except:', e)
finally:
    print('finally...')
print('END')

#结果
try...             #try中出现错误,不会继续执行后面的程序
except: division by zero      #指出错误原因
finally...
END

当我们认为某些代码可能会出错时,就可以用try来运行这段代码,如果执行出错,则后续代码不会继续执行,而是直接跳转至错误处理代码,即except语句块,执行完except后,如果有finally语句块,则执行finally语句块,至此,执行完毕。

可以有多个except来捕获不同类型的错误:

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


#结果
try...
ValueError: invalid literal for int() with base 10: 'a'
finally...
END

如果没有错误发生,可以在except语句块后面加一个else,当没有错误发生时,会自动执行else语句:

try:
    print('try...')
    r = 10 / int('2')
    print('result:', r)
except ValueError as e:
    print('ValueError:', e)
except ZeroDivisionError as e:
    print('ZeroDivisionError:', e)
else:         ###
    print('no error!')
finally:
    print('finally...')
print('END')


#结果
try...
result: 5.0
no error!
finally...
END

使用try…except捕获错误还有一个巨大的好处,就是可以跨越多层调用

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

函数main()调用bar()bar()调用foo(),结果foo()出错了,这时,只要main()捕获到了,就可以处理

1.2 调用栈

如果错误没有被捕获,它就会一直往上抛,最后被Python解释器捕获,打印一个错误信息,然后程序退出。如下:

def foo(s):
    return 10 / int(s)

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

def main():
    bar('0')

main()

运行结果:

$ python3 err.py
Traceback (most recent call last):   #告诉我们这是错误的跟踪信息
  File "err.py", line 11, in <module>
    main()                          # 调用main()出错了,但原因在第九行
  File "err.py", line 9, in main    
    bar('0')                       #调用bar('0')出错了,但原因是第6行:
  File "err.py", line 6, in bar
    return foo(s) * 2              #return foo(s) * 2这个语句出错了,但这还不是最终原因
  File "err.py", line 3, in foo
    return 10 / int(s)            #return 10 / int(s)这个语句出错了,这是错误产生的源头!!!
ZeroDivisionError: division by zero    #最终错误源头

1.3 抛出错误

Python的内置函数会抛出很多类型的错误,我们自己编写的函数也可以抛出错误。但只有在必要的时候才定义我们自己的错误类型

首先根据需要,可以定义一个错误的class,选择好继承关系,然后,用raise语句抛出一个错误的实例

class FooError(ValueError):
    pass

def foo(s):
    n = int(s)
    if n==0:
        raise FooError('invalid value: %s' % s)
    return 10 / n

foo('0')

执行,可以最后跟踪到我们自己定义的错误

$ python3 err_raise.py 
Traceback (most recent call last):
  File "err_throw.py", line 11, in <module>
    foo('0')
  File "err_throw.py", line 8, in foo
    raise FooError('invalid value: %s' % s)
__main__.FooError: invalid value: 0

练习
运行下面的代码,根据异常信息进行分析,定位出错误源头,并修复:

from functools import reduce

def str2num(s):
  ############重点部分
    try:
       return int(s)
    except ValueError:
       print('ValueError:input float!')
       return float(s)
  ############
def calc(exp):
    ss = exp.split('+')
    ns = map(str2num, ss)
    return reduce(lambda acc, x: acc + x, ns)

def main():
    r = calc('100 + 200 + 345')
    print('100 + 200 + 345 =', r)
    r = calc('99 + 88 + 7.6')
    print('99 + 88 + 7.6 =', r)

main()
100 + 200 + 345 = 645
ValueError:input float
99 + 88 + 7.6 = 194.6

2.调试

突然想起朋友圈的程序员同学一句话:“coding all day,debugging all night”,可以说是完美地诠释了调试以及修复bug的重要性。

在原文中,廖老师提到了好几种python自有的调试方法:

  1. print(用了这么久才发现真是粗暴呀!)
  2. 断言(不知为何我用了没有效果?)
  3. logging(老师评价的最牛B的!但是初学还是觉得麻烦)
  4. pdbpdb.set_trace()(无感)

直到最后出现IDE才眼前一亮,要比较爽地设置断点、单步执行,支持调试功能的IDE绝对是必备选项啊!目前比较好的Python IDE有:

Visual Studio Codehttps://code.visualstudio.com/,需要安装Python插件。
PyCharmhttp://www.jetbrains.com/pycharm/

本人下载的IDE是PyCharm,关于调试方法,找到了一篇详细的指导教程:新手必会,pycharm的调试功能(史上最详篇)

总结文中学习到的调试方法:

  1. 绿色小瓢虫:所有代码debug模式
  2. 断点调试

设置断点(代码前红点)
绿色瓢虫(进入debug)
F7快捷键(进入子函数并且继续单步执行
F8快捷键单步调试
F9快捷键(跳过当前,直接运行到下一断点处
Shift+F8快捷键:假如进入了一个函数体中,想跳出当前函数体内,返回到调用此函数的地方。

3.单元测试

单元测试是用来对一个模块、一个函数或者一个类来进行正确性检验的测试工作

比如对函数abs(),我们可以编写出以下几个测试用例:

  1. 输入正数,比如1、1.2、0.99,期待返回值与输入相同;
  2. 输入负数,比如-1、-1.2、-0.99,期待返回值与输入相反;
    输入0,期待返回0;
  3. 输入非数值类型,比如None、[]、{},期待抛出TypeError。

把上面的测试用例放到一个测试模块里,就是一个完整的单元测试。

练习
对Student类编写单元测试,结果发现测试不通过,请修改Student类,让测试通过:
a.py文件:含待测试模块

class Student(object):
    def __init__(self, name, score):
        self.name = name
        self.score = score
    def get_grade(self):
        if self.score >= 60 and self.score<80:
            return 'B'
        if self.score >= 80  and self.score<=100:
            return 'A'
        if self.score>=0 and self.score<60:
            return 'C'
        else:
            raise ValueError ('This score is error.')

a_test.py文件:含测试模块

import unittest   #编写单元测试,需要引入Python自带的unittest模块

from a import Student   #原模块中待测试类导入测试模块

 #单元测试模块
class TestStudent(unittest.TestCase):  

    def test_80_to_100(self):
        s1 = Student('Bart', 80)
        s2 = Student('Lisa', 100)
        self.assertEqual(s1.get_grade(), 'A')   #断言函数返回结果是否与A相等
        self.assertEqual(s2.get_grade(), 'A')

    def test_60_to_80(self):
        s1 = Student('Bart', 60)
        s2 = Student('Lisa', 79)
        self.assertEqual(s1.get_grade(), 'B')
        self.assertEqual(s2.get_grade(), 'B')

    def test_0_to_60(self):
        s1 = Student('Bart', 0)
        s2 = Student('Lisa', 59)
        self.assertEqual(s1.get_grade(), 'C')
        self.assertEqual(s2.get_grade(), 'C')

    def test_invalid(self):
        s1 = Student('Bart', -1)
        s2 = Student('Lisa', 101)
        with self.assertRaises(ValueError):   #输入错误的分数时,我们期待抛出ValueError
            s1.get_grade()
        with self.assertRaises(ValueError):
            s2.get_grade()

if __name__ == '__main__':   
    unittest.main()    # 运行单元测试
  • 关于if _name_ == ‘__main__’:
    具体可参考博客:如何快速简单粗暴地理解Python中的if name == ‘main

重点:
当.py文件被直接运行时,if _name_ == '_main__'之下的代码块将被运行;
当.py文件以模块形式被导入时,if _name_ == '
_main__'之下的代码块不被运行。

  • 也可以将a,b文件代码放在一个文件中运行,略去from a import Student即可。

4.文档测试

如果你经常阅读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的交互式环境下输入并执行,结果与文档中的示例代码显示的一致。

Python内置的“文档测试”(doctest)模块可以直接提取注释中的代码并执行测试。

练习
对函数fact(n)编写doctest并执行:

def fact(n):
 #黄色字体部分为文档测试模块
    '''       #三个冒号   
    Calculate 1*2*...*n
    
    >>> fact(1)
    1
    >>> fact(10)
    3628800
    >>> fact(-1)      #错误部分设置
    Traceback (most recent call last):
     ...              #三个点
    ValueError
    '''
    if n < 1:
        raise ValueError()
    if n == 1:  
        return 1
    else:
        return   n * fact(n - 1)

if __name__ == '__main__':      #运行文档测试!!!
    import doctest
    doctest.testmod()

当注释发生错误,比如把fact(10)的结果置换成10000,运行结果如下:

File "D:/Pythonhello/b.py", line 7, in __main__.fact
Failed example:
    fact(10)
Expected:
    10000
Got:
    3628800
**********************************************************************
1 items had failures:
   1 of   3 in __main__.fact
***Test Failed*** 1 failures.

用“文档测试”(doctest)模块可以检查所写的注释代码是否有错。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值