本章主要讲了python程序的调试,当程序有BUG或异常的时候,我们如何调试代码找出问题点。其实在本章之前的章节我们做练习的时候都会遇到各种各样的错语和异常,最初当不知道程序哪里出错的情况下不可否认的都使用了print语句进行输出并调试代码。没错print也是调试代码的一种工具,直观简单,但也有缺点,就是调试好后要删除print语句,也是件麻烦事,于是就有了本章介绍的assert(断言),logging(日志)以及各种调试工具的出现。
首先来回顾一下python的异常。
一.python常见的异常类型
异常名称 | 描述 |
BaseException | 所有异常的基类 |
SystemExit | 解释器请求退出 |
KeyboardInterrupt | 用户中断执行(通常是输入^C) |
Exception | 常规错误的基类 |
StopIteration | 迭代器没有更多的值 |
GeneratorExit | 生成器(generator)发生异常来通知退出 |
StandardError | 所有的内建标准异常的基类 |
ArithmeticError | 所有数值计算错误的基类 |
FloatingPointError | 浮点计算错误 |
OverflowError | 数值运算超出最大限制 |
ZeroDivisionError | 除(或取模)零 (所有数据类型) |
AssertionError | 断言语句失败 |
AttributeError | 对象没有这个属性 |
EOFError | 没有内建输入,到达EOF 标记 |
EnvironmentError | 操作系统错误的基类 |
IOError | 输入/输出操作失败 |
OSError | 操作系统错误 |
WindowsError | 系统调用失败 |
ImportError | 导入模块/对象失败 |
LookupError | 无效数据查询的基类 |
IndexError | 序列中没有此索引(index) |
KeyError | 映射中没有这个键 |
MemoryError | 内存溢出错误(对于Python 解释器不是致命的) |
NameError | 未声明/初始化对象 (没有属性) |
UnboundLocalError | 访问未初始化的本地变量 |
ReferenceError | 弱引用(Weak reference)试图访问已经垃圾回收了的对象 |
RuntimeError | 一般的运行时错误 |
NotImplementedError | 尚未实现的方法 |
SyntaxError | Python 语法错误 |
IndentationError | 缩进错误 |
TabError | Tab 和空格混用 |
SystemError | 一般的解释器系统错误 |
TypeError | 对类型无效的操作 |
ValueError | 传入无效的参数 |
UnicodeError | Unicode 相关的错误 |
UnicodeDecodeError | Unicode 解码时的错误 |
UnicodeEncodeError | Unicode 编码时错误 |
UnicodeTranslateError | Unicode 转换时错误 |
Warning | 警告的基类 |
DeprecationWarning | 关于被弃用的特征的警告 |
FutureWarning | 关于构造将来语义会有改变的警告 |
OverflowWarning | 旧的关于自动提升为长整型(long)的警告 |
PendingDeprecationWarning | 关于特性将会被废弃的警告 |
RuntimeWarning | 可疑的运行时行为(runtime behavior)的警告 |
SyntaxWarning | 可疑的语法的警告 |
UserWarning | 用户代码生成的警告 |
二.python异常处理
python处理异常的完整语句如下:实际程序中并不是每个语句都要用上,可以组合起来用。
try:
try_suite
except Exception1,Exception2,...,Argument:
exception_suite
...... #other exception block
else:
no_exceptions_detected_suite
finally:
always_execute_suite
具体用法请参见:
python2:http://www.runoob.com/python/python-exceptions.html
python3:http://www.runoob.com/python3/python3-errors-execptions.html
三.抛出异常
抛出异常使用raise语句。
语法:raise Exception(‘出错信息的字符串’)
此语句直接在交互模式下运行则会直接报错并返回Exception函数中的字符串。一般情况下raise语句用在函数中,try和except语句在调用该函数的代码中,在except语句中接收exception函数作为参数。
except Exception as err:
print(‘An exception happened:’ + str(err)
四.traceback模块
使用traceback模块可以跟踪异常并得到更祥细的出错信息,其实程序如果没有进行异常处理但是确实有异常存在的时候,从屏幕输出的信息来看,我们都会看很祥细的出错信息,包括哪一行出错,是什么错误等等,这应该就是traceback模块返回的信息,只是python本身帮我们抛出了异常。
在交互模式下,先导入traceback模块,通过help()就可以查看traceback的帮助信息。
import traceback
help(traceback)
2个常用函数
format_exc()返回字符串,print_exc()则直接给打印出来
五.断言assert
使用assert语句检查,如果失败就抛出异常
语法:
assert 条件,’异常信息’
例如:
In [16]: p = 1
In [17]: assert p==0,'error'
---------------------------------------------------------------------------
AssertionError Traceback (most recent call last)
<ipython-input-17-11c0bd0cc9d4> in <module>()
----> 1 assert p==0,'error'AssertionError: error
六.logging日志模块
Python的logging模块提供了通用的日志系统,可以方便第三方模块或者是应用使用。这个模块提供不同的日志级别,并可以采用不同的方式记录日志,共有5个级别
级别 | 日志函数 | 描述 |
DEBUG | logging.debug() | 最低级别,用于小细节。通常只有在诊断问题时,你才会关心这些信息 |
INFO | logging.info() | 用于记录程序中一般的事件信息,或确认一切工作正常 |
WARNING | logging.warning() | 用于表示可能的问题,它不阻止程序的工作,但将来可能会 |
ERROR | logging.error() | 用于记录错误,它导致程序做某事失败 |
CRITICAL | logging.critical() | *别。用于表示致命的错误,它导致或将要导致程序完全停止 |
使用语法:
In [18]: import logging
In [19]: logging.basicConfig(level=logging.debug,format='%(asctime)s - %(levelname)s - %(message)s')
In [20]: logging.debug('some debugging details')
2017-04-28 23:36:39,795 - DEBUG - some debugging details
format,定义了最终log信息的顺序,结构和内容。
%(name)s Logger的名字
%(levelname)s 文本形式的日志级别
%(message)s 用户输出的消息
%(asctime)s 字符串形式的当前时间。默认格式是 “2003-07-08 16:49:45,896”。逗号后面的是毫秒
%(levelno)s 数字形式的日志级别
%(pathname)s 调用日志输出函数的模块的完整路径名,可能没有
%(filename)s 调用日志输出函数的模块的文件名
%(module)s 调用日志输出函数的模块名
%(funcName)s 调用日志输出函数的函数名
%(lineno)d 调用日志输出函数的语句所在的代码行
%(created)f 当前时间,用UNIX标准的表示时间的浮 点数表示
%(relativeCreated)d 输出日志信息时的,自Logger创建以 来的毫秒数
%(thread)d 线程ID。可能没有
%(threadName)s 线程名。可能没有
%(process)d 进程ID。可能没有
禁用日志
In [21]: logging.disable(logging.debug)
将日志写入文件
In [22]: logging.basicConfig(filename='mylog.txt',level=logging.debug,format='%(asctime)s - %(levelname)s - %(message)s')
7.实践项目参考答案
#!/usr/bin/env python3.4
# debug.py
# create by mfyang 2017-04-28
import random
import logging
logging.basicConfig(level=logging.DEBUG,format=' %(asctime)s - %(levelname)s -%(message)s')
#logging.disable(logging.DEBUG)
logging.debug('start of program')
guess = ''
while guess not in ('heads','tails'):
print('Guess the coin toss! Enter heads or tails:')
guess = input()
logging.debug('you input is:' + str(guess))
toss = random.randint(0,1)
coinresult = ''
if toss == 0:
coinresult = 'tails'
if toss == 1:
coinresult = 'heads'
logging.debug('toss is(0 is tails 1 is heads):' + coinresult)
if coinresult == guess:
print('you got it!')
else:
print('Nope! Guess again')
guess = input()
logging.debug('you input is:' + str(guess))
if coinresult == guess:
print('You got it!')
else:
print('Nope. You are really bad at this game.')
logging.debug('end of debug')