@zhangyu756897669
2017-09-20T14:28:34.000000Z
字数 8391
阅读 553
python官方文档
当Python遇到错误时,会产生一个名为traceback的错误信息。追溯包括错误消息,导致错误的行的行号以及导致错误的函数调用的顺序。这个调用序列称为调用堆栈。
在IDLE中打开一个新的文件编辑器窗口,输入以下程序,并将其另存为errorExample.py:
def spam():
bacon()
def bacon():
raise Exception('This is the error message.')
spam()
当您运行errorExample.py时,输出将如下所示:
Traceback (most recent call last):
File "errorExample.py", line 7, in
spam()
File "errorExample.py", line 2, in spam
bacon()
File "errorExample.py", line 5, in bacon
raise Exception('This is the error message.')
Exception: This is the error message.
从追溯中可以看到错误发生在第5行,在bacon()函数中。对bacon()的这个特定调用来自第2行,在spam()函数中,这又被称为第7行。在可以从多个地方调用函数的程序中,调用堆栈可以帮助您确定哪个调用导致错误。
每当一个引发的异常被处理时,Python都会显示回溯。但是您也可以通过调用traceback.format_exc()获取它作为一个字符串。如果您希望来自异常的追溯信息,而且希望使用except语句来正常处理异常,则此函数很有用。在调用此函数之前,需要导入Python的追溯模块。
例如,您可以将异常信息写入日志文件,并保持程序运行,而不是在异常发生时正确地破坏程序。稍后,当您准备好调试程序时,可以查看日志文件。在交互式shell中输入以下内容:
import traceback
try:
raise Exception('This is the error message.')
except:
errorFile = open('errorInfo.txt', 'w')
errorFile.write(traceback.format_exc())
errorFile.close()
print('The traceback info was written to errorInfo.txt.')
The traceback info was written to errorInfo.txt.
一个论断是一个健全检查,以确保你的代码没有做明显的错误。这些健全检查由论断语句执行。如果理性检查失败,则会引发AssertionError异常。在代码中,assert语句包含以下内容:
podBayDoorStatus = 'open'
assert podBayDoorStatus == 'open', 'The pod bay doors need to be "open".'
podBayDoorStatus = 'I\'m sorry, Dave. I\'m afraid I can\'t do that.'
assert podBayDoorStatus == 'open', 'The pod bay doors need to be "open".'
AssertionError Traceback (most recent call last)
in ()
2 assert podBayDoorStatus == 'open', 'The pod bay doors need to be "open".'
3 podBayDoorStatus = 'I\'m sorry, Dave. I\'m afraid I can\'t do that.'
----> 4 assert podBayDoorStatus == 'open', 'The pod bay doors need to be "open".'AssertionError: The pod bay doors need to be "open".
在这里,我们将podBayDoorStatus设置为“open”,所以从现在开始,我们完全期望该变量的值为“open”。在使用此变量的程序中,我们可能已经编写了大量代码,假定值为“open”代码,这取决于它是“打开的”,以便按照我们的期望工作。所以我们添加一个论断,以确保我们是正确的假设podBayDoorStatus是'开放'。在这里,我们包括“荚舱门需要”开放的信息。因此,如果断言失败,那么很容易看出有什么问题。
后来说我们明确的指出了podBayDoorStatus的一个值,但是不要在许多代码行中注意到。这个论断抓住了这个错误,并清楚地告诉我们是怎么回事。
在简单的英语中,一个assert语句说:“我声称这个条件成立,如果没有,程序中有一个bug。”与异常不同,你的代码不应该用try和except来处理assert语句;如果论断失败,您的程序应该崩溃。通过快速失败,您可以缩短错误原始原因和首次注意到错误之间的时间。这将减少在找到导致该错误的代码之前必须检查的代码量。
论断用于程序员错误,而不是用户错误。对于可从(例如未找到的文件或用户输入无效数据)中恢复的错误,引发异常,而不是使用assert语句检测异常。
说你正在建造一个交通灯模拟程序。代表交叉口停车位的数据结构是一个带有“ns”和“ew”键的字典,分别是面向南北和东西的停车位。这些键的值将是“绿色”,“黄色”或“红色”之一。代码看起来像这样:
market_2nd = {'ns': 'green', 'ew': 'red'}
mission_16th = {'ns': 'red', 'ew': 'green'}
这两个变量将用于市场街和第二街,特派团街和十六街的交汇处。要启动项目,您需要编写一个switchLights()函数,它将交叉字典作为参数,并切换灯光。
首先,你可能会认为switchLights()应该简单地将每个灯切换到下一个颜色:任何'绿色'值应该改为'黄色','黄色'值应该改为'红色','红色'值应改为“绿色”。实现这个想法的代码可能如下所示:
market_2nd = {'ns': 'green', 'ew': 'red'}
mission_16th = {'ns': 'red', 'ew': 'green'}
def switchLights(stoplight):
for key in stoplight.keys():
if stoplight[key] == 'green':
stoplight[key] = 'yellow'
elif stoplight[key] == 'yellow':
stoplight[key] = 'red'
elif stoplight[key] == 'red':
stoplight[key] = 'green'
switchLights(market_2nd)
您可能已经看到这个代码的问题,但是让我们假装你写了其余的模拟代码,数千行长,没有注意到。当你最终运行模拟时,程序不会崩溃 - 但你的虚拟汽车!
由于你已经写了程序的其余部分,所以你不知道错误可能在哪里。也许这是在模拟汽车的代码或模拟虚拟驱动程序的代码中。可能需要几个小时才能将bug追溯到switchLights()函数。
但是如果在写入switchLights()时添加了论断来检查至少有一个指示灯始终为红色,则可能在函数底部包含以下内容:
assert 'red' in stoplight.values(), 'Neither light is red! ' + str(stoplight)
有了这个论断,你的程序会崩溃这个错误消息:
Traceback (most recent call last):
File "carSim.py", line 14, in
switchLights(market_2nd)
File "carSim.py", line 13, in switchLights
assert 'red' in stoplight.values(), 'Neither light is red! ' + str(stoplight)
❶ AssertionError: Neither light is red! {'ns': 'yellow', 'ew': 'green'}
这里的重要线是AssertionError❶。虽然您的程序崩溃并不理想,但它立即指出,理智检查失败:流量的任何一个方向都没有红灯,这意味着流量可能会两路走来。通过程序执行的早期失败,您可以节省大量未来的调试工作量。
通过在运行Python时传递-O选项可以禁用断言。当您完成编写和测试您的程序并且不希望通过执行完整性检查来减慢速度时,这很有用(尽管大多数时间的assert语句不会引起明显的速度差异)。论断是为了开发而不是最终产品。当你将你的程序交给别人运行时,它应该没有错误,而不需要理智的检查。
如果您在代码中放置一个print()语句,以便在程序运行时输出一些变量的值,那么您已经使用一种记录形式来调试代码。记录是了解程序中发生的情况以及其发生的顺序的好方法。 Python的日志记录模块可以轻松创建您编写的自定义消息的记录。这些日志消息将描述程序执行到达日志记录函数调用并列出在该时间点指定的任何变量。另一方面,缺少的日志消息表示代码的一部分被跳过,从不执行。
要使记录模块在程序运行时在屏幕上显示日志消息,请将以下内容复制到程序的顶部
import logging
logging.basicConfig(level=logging.DEBUG, format=' %(asctime)s - %(levelname)s
- %(message)s')
您不需要太担心这是如何工作的,但基本上,当Python记录事件时,它会创建一个LogRecord对象,该对象包含有关该事件的信息。日志记录模块的basicConfig()函数允许您指定要查看的LogRecord对象的详细信息,以及您希望显示这些详细信息的方式。
说你写了一个函数来计算一个数字的阶乘。在数学中,阶乘4是1×2×3×4或24.因子7是1×2×3×4×5×6×7或者5,040。打开一个新的文件编辑器窗口并输入以下代码。它有一个错误,但你也将输入几个日志消息,以帮助你自己弄清楚出了什么问题。将程序保存为factorialLog.py。
import logging
logging.basicConfig(level=logging.DEBUG, format=' %(asctime)s - %(levelname)s - %(message)s')
logging.debug('Start of program')
def factorial(n):
logging.debug('Start of factorial(%s)' % (n))
total = 1
for i in range(n + 1):
total *= i
logging.debug('i is ' + str(i) + ', total is ' + str(total))
logging.debug('End of factorial(%s)' % (n))
return total
print(factorial(5))
logging.debug('End of program')
2017-09-20 21:37:10,892 - DEBUG - Start of program
2017-09-20 21:37:10,896 - DEBUG - Start of factorial(5)
2017-09-20 21:37:10,901 - DEBUG - i is 0, total is 0
2017-09-20 21:37:10,904 - DEBUG - i is 1, total is 0
2017-09-20 21:37:10,907 - DEBUG - i is 2, total is 0
2017-09-20 21:37:10,911 - DEBUG - i is 3, total is 0
2017-09-20 21:37:10,914 - DEBUG - i is 4, total is 0
2017-09-20 21:37:10,917 - DEBUG - i is 5, total is 0
2017-09-20 21:37:10,920 - DEBUG - End of factorial(5)
2017-09-20 21:37:10,924 - DEBUG - End of program0
在这里,当我们要打印日志信息时,我们使用logging.debug()函数。这个debug()函数将调用basicConfig(),并打印一行信息。这些信息将采用我们在basicConfig()中指定的格式,并将包含我们传递给debug()的消息。打印(阶乘(5))调用是原始程序的一部分,因此即使记录消息被禁用,也会显示结果。
阶乘()函数返回0作为5的阶乘,这是不正确的。 for循环应该将总共乘以1到5之间的数字。但是logging.debug()显示的日志消息显示i变量从0开始,而不是1。由于零次任何事情都为零,所以其余的迭代也有错误的总价值。记录消息提供了一些面包屑,可以帮助您了解何时出现问题。
将for i in range(n + 1):
改为 ```for i in range(1, n + 1):````,
import logging
logging.basicConfig(level=logging.DEBUG, format=' %(asctime)s - %(levelname)s - %(message)s')
logging.debug('Start of program')
def factorial(n):
logging.debug('Start of factorial(%s)' % (n))
total = 1
for i in range(1, n + 1):
total *= i
logging.debug('i is ' + str(i) + ', total is ' + str(total))
logging.debug('End of factorial(%s)' % (n))
return total
print(factorial(5))
logging.debug('End of program')
2017-09-20 21:41:43,595 - DEBUG - Start of program
2017-09-20 21:41:43,599 - DEBUG - Start of factorial(5)
2017-09-20 21:41:43,602 - DEBUG - i is 1, total is 1
2017-09-20 21:41:43,605 - DEBUG - i is 2, total is 2
2017-09-20 21:41:43,607 - DEBUG - i is 3, total is 6
2017-09-20 21:41:43,610 - DEBUG - i is 4, total is 24
2017-09-20 21:41:43,613 - DEBUG - i is 5, total is 120
2017-09-20 21:41:43,615 - DEBUG - End of factorial(5)
2017-09-20 21:41:43,617 - DEBUG - End of program120
你可以看到logging.debug()调用打印出来的不仅仅是传递给它们的字符串,还有一个时间戳和DEBUG这个字。
日志记录级别提供了一种通过重要性对日志消息进行分类的方法。表10-1中描述了五个记录级别,从最少到最重要。可以使用不同的记录功能在每个级别记录消息。
水平 | 记录功能 | 描述 |
---|---|---|
DEBUG | logging.debug() | 最低水平。用于小细节。通常只有在诊断问题时才关心这些消息。 |
INFO | logging.info() | 用于记录您的程序中的一般事件的信息,或确认事情在程序中的工作。 |
WARNING | logging.warning() | 用于表示潜在的问题,不会阻止程序的工作,但可能会在将来这样做。 |
ERROR | logging.error() | 用于记录导致程序无法做某事的错误。 |
CRITICAL | logging.critical() | 最高级别。用于指示导致或即将导致程序完全停止运行的致命错误。 |
您的日志消息作为字符串传递给这些函数。记录级别是建议。最终,您可以决定您的日志消息属于哪个类别。在交互式shell中输入以下内容:
import logging
logging.basicConfig(level=logging.DEBUG, format=' %(asctime)s -%(levelname)s - %(message)s')
logging.debug('Some debugging details.')
2017-09-20 21:56:17,352 - DEBUG - Some debugging details.
logging.info('The logging module is working.')
2017-09-20 21:57:34,372 - INFO - The logging module is working.
logging.warning('An error message is about to be logged.')
2017-09-20 21:58:08,602 - WARNING - An error message is about to be logged.
logging.error('An error has occurred.')
2017-09-20 21:58:47,944 - ERROR - An error has occurred.
logging.critical('The program is unable to recover!')
2017-09-20 21:59:14,085 - CRITICAL - The program is unable to recover!
记录级别的好处是您可以更改要查看的日志消息的优先级。将logging.DEBUG传递给basicConfig()函数的level关键字参数将显示所有日志记录级别的消息(DEBUG为最低级别)。但是,在开发您的程序之后,您可能只对错误感兴趣。在这种情况下,您可以将basicConfig()的level参数设置为logging.ERROR。这将仅显示ERROR和CRITICAL消息,并跳过DEBUG,INFO和WARNING消息。
调试程序后,您可能不希望所有这些日志消息都会使屏幕变得混乱。 logging.disable()函数禁用这些功能,因此您无需进入程序并手动删除所有日志记录。您只需传递logging.disable()日志记录级别,它将会抑制该级别或更低级别的所有日志消息。因此,如果要完全禁用日志记录,只需将logging.disable(logging.CRITICAL)添加到您的程序中即可。例如,在交互式shell中输入以下内容:
import logging
logging.basicConfig(level=logging.INFO, format=' %(asctime)s -%(levelname)s - %(message)s')
logging.critical('Critical error! Critical error!')
2017-09-20 22:00:55,565 - CRITICAL - Critical error! Critical error!
logging.disable(logging.CRITICAL)
logging.critical('Critical error! Critical error!')
logging.error('Error! Error!')
由于logging.disable()将禁用所有消息,因此您可能希望将其添加到程序中的导入记录代码行附近。这样,您可以轻松找到它来注释或取消注释该调用以根据需要启用或禁用日志记录消息。
您可以将它们写入文本文件,而不是将屏幕上的日志消息显示出来。 logging.basicConfig()函数使用filename关键字参数,如下所示:
import logging
logging.basicConfig(filename='myProgramLog.txt', level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s')
日志消息将保存到myProgramLog.txt。在记录消息有帮助的同时,它们可能会使您的屏幕混乱,从而难以读取程序的输出。将记录消息写入文件将保持屏幕清除并存储消息,以便您可以在运行程序后阅读它们。您可以在任何文本编辑器中打开此文本文件,如记事本或TextEdit。