Python学习之错误调试和测试

Python学习目录python

  1. 在Mac下使用Python3
  2. Python学习之数据类型
  3. Python学习之函数
  4. Python学习之高级特性
  5. Python学习之函数式编程
  6. Python学习之模块
  7. Python学习之面向对象编程
  8. Python学习之面向对象高级编程
  9. Python学习之错误调试和测试
  10. Python学习之IO编程
  11. Python学习之进程和线程
  12. Python学习之正则
  13. Python学习之经常使用模块
  14. Python学习之网络编程

在程序运行过程当中,总会遇到各类各样的错误,Python内置了一套异常处理机制,来帮助咱们进行错误处理。编程

debug

错误处理

在程序运行的过程当中,若是发生了错误,能够事先约定返回一个错误代码,这样,就能够知道是否有错,以及出错的缘由。在操做系统提供的调用中,返回错误码很是常见。好比打开文件的函数open(),成功时返回文件描述符(就是一个整数),出错时返回-1网络

用错误码来表示是否出错十分不便,由于函数自己应该返回的正常结果和错误码混在一块儿,形成调用者必须用大量的代码来判断是否出错,因此高级语言一般都内置了一套try...except...finally...的错误处理机制,Python也不例外。eclipse

try

try:
    print('try...')
    r = 10 / 0
    print('result:', r)
except ZeroDivisionError as e:
    print('except:', e)
finally:
    print('finally...')
print('END')
复制代码

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

Python的错误其实也是class,全部的错误类型都继承自BaseException,因此在使用except时须要注意的是,它不但捕获该类型的错误,还把其子类也“一网打尽”。函数

调用栈

若是错误没有被捕获,它就会一直往上抛,最后被Python解释器捕获,打印一个错误信息,而后程序退出。来看看err.py工具

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

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

def main():
    bar('0')

main()
复制代码

执行,结果以下:post

$ python3 err.py
Traceback (most recent call last):
  File "err.py", line 11, in <module>
    main()
  File "err.py", line 9, in main
    bar('0')
  File "err.py", line 6, in bar
    return foo(s) * 2
  File "err.py", line 3, in foo
    return 10 / int(s)
ZeroDivisionError: division by zero
复制代码

咱们从上往下能够看到整个错误的调用函数链。单元测试

记录错误

若是不捕获错误,天然可让Python解释器来打印出错误堆栈,但程序也被结束了。既然咱们能捕获错误,就能够把错误堆栈打印出来,而后分析错误缘由,同时,让程序继续执行下去。学习

Python内置的logging模块能够很是容易地记录错误信息:

# err_logging.py

import 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.exception(e)

main()
print('END')
复制代码

执行上述模块:

$ python3 err_logging.py
ERROR:root:division by zero
Traceback (most recent call last):
  File "err_logging.py", line 13, in main
    bar('0')
  File "err_logging.py", line 9, in bar
    return foo(s) * 2
  File "err_logging.py", line 6, in foo
    return 10 / int(s)
ZeroDivisionError: division by zero
END
复制代码

抛出错误

由于错误是class,捕获一个错误就是捕获到该class的一个实例。所以,错误并非凭空产生的,而是有意建立并抛出的。Python的内置函数会抛出不少类型的错误,咱们本身编写的函数也能够抛出错误。

若是要抛出错误,首先根据须要,能够定义一个错误的class,选择好继承关系,而后,用raise语句抛出一个错误的实例:

# 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')
复制代码

执行,能够最后跟踪到咱们本身定义的错误:

$ 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
复制代码

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

# 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()
复制代码

bar()函数中,咱们明明已经捕获了错误,可是,打印一个ValueError!后,又把错误经过raise语句抛出去了,这不有病么?

其实这种错误处理方式不但没病,并且至关常见。捕获错误目的只是记录一下,便于后续追踪。可是,因为当前函数不知道应该怎么处理该错误,因此,最恰当的方式是继续往上抛,让顶层调用者去处理。比如一个员工处理不了一个问题时,就把问题抛给他的老板,若是他的老板也处理不了,就一直往上抛,最终会抛给CEO去处理。

raise语句若是不带参数,就会把当前错误原样抛出。此外,在exceptraise一个Error,还能够把一种类型的错误转化成另外一种类型:

try:
    10 / 0
except ZeroDivisionError:
    raise ValueError('input error!')
复制代码

调试

程序能一次写完并正常运行的几率很小,基本不超过1%。总会有各类各样的bug须要修正。有的bug很简单,看看错误信息就知道,有的bug很复杂,咱们须要知道出错时,哪些变量的值是正确的,哪些变量的值是错误的,所以,须要一整套调试程序的手段来修复bug。

print()

print()把可能有问题的变量打印出来看看。

断言

def foo(s):
    n = int(s)
    assert n != 0, 'n is zero!'
    return 10 / n

def main():
    foo('0')
复制代码

assert的意思是,表达式n != 0应该是True,不然,根据程序运行的逻辑,后面的代码确定会出错。

若是断言失败,assert语句自己就会抛出AssertionError

logging

logging容许你指定记录信息的级别,有debuginfowarningerror等几个级别,当咱们指定level=INFO时,logging.debug就不起做用了。同理,指定level=WARNING后,debuginfo就不起做用了。这样一来,你能够放心地输出不一样级别的信息,也不用删除,最后统一控制输出哪一个级别的信息。

logging的另外一个好处是经过简单的配置,一条语句能够同时输出到不一样的地方,好比console和文件。

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

s = '0'
n = int(s)
logging.info('n = %d' % n)
print(10 / n)
复制代码

pdb

Python的调试器pdb,让程序以单步方式运行,能够随时查看运行状态。咱们先准备好程序:

# err.py
s = '0'
n = int(s)
print(10 / n)
复制代码

而后启动:

$ python -m pdb err.py
> /Users/michael/Github/learn-python3/samples/debug/err.py(2)<module>()
-> s = '0'
复制代码

以参数-m pdb启动后,pdb定位到下一步要执行的代码-> s = '0'。输入命令l来查看代码:

(Pdb) l
  1     # err.py
  2  -> s = '0'
  3     n = int(s)
  4     print(10 / n)
复制代码

输入命令n能够单步执行代码。

pdb.set_trace()

这个方法也是用pdb,可是不须要单步执行,咱们只须要import pdb,而后,在可能出错的地方放一个pdb.set_trace(),就能够设置一个断点:

# err.py
import pdb

s = '0'
n = int(s)
pdb.set_trace() # 运行到这里会自动暂停
print(10 / n)
复制代码

运行代码,程序会自动在pdb.set_trace()暂停并进入pdb调试环境,能够用命令p查看变量,或者用命令c继续运行。

IDE

目前比较好的Python IDE有:

isual Studio Code:code.visualstudio.com/,须要安装Python插件。

PyCharm:www.jetbrains.com/pycharm/

另外,Eclipse加上pydev插件也能够调试Python程序。

单元测试

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

文档测试

若是你常常阅读Python的官方文档,能够看到不少文档都有示例代码。

能够把这些示例代码在Python的交互式环境下输入并执行,结果与文档中的示例代码显示的一致。

这些代码与其余说明能够写在注释中,而后,由一些工具来自动生成文档。

下一篇:Python学习之IO编程

相关文章
相关标签/搜索