10.1 抛出异常(raise)
当Python试图执行无效代码时,则抛出异常。
之前介绍过可使用 try-except 语句处理异常,使程序从预期的异常中恢复。
现介绍使用 raise 语句在代码中抛出异常:“停止运行本函数,将程序执行到except语句”。
使用raise语句,抛出异常,其包含:
1. raise关键字;
2. 对Exception函数的调用;
3.传递给Eception函数的字符串(有用的出错信息)。
若无 try-except 语句覆盖 raise 语句,程序会崩溃,显示异常出错信息。 通常为调用函数的代码知道如何处理异常,而非函数本身处理。故常见 raise 在函数中, try-except 在调用函数的代码中。 如:
# boxPrint.py
def boxPrint(symbol, width, height):
if len(symbol) != 1:
raise Exception('Symbol must be a single character string.')
if width <= 2:
raise Exception('Width must be greater than 2.')
if height <= 2:
raise Exception('Height must be greater than 2.')
print(symbole * width)
for i in range(height - 2):
print(symbol + (' ' * (width - 2) ) + symbol)
for sym, w, h, in (('*', 4, 4), ('0', 20, 5), ('x', 1, 3), ('ZZ', 3, 3)):
try:
boxPrint(sym, w, h)
except Exception as err: # 将Exception对象保存在变量err中。
print('An exception happened: ' + str(err)) # 将Exception传递给str(),转换为字符串,得到用户友好的信息
10.2 取得反向跟踪的字符串
Python生成的错误信息,称为反向跟踪(含出错信息、导致错误的代码行号、导致错误的函数调用的序列(调用栈))。
如#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<module>
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(),spam()在第7行被调用。在从多次调用函数的程序中,调用栈可确定导致错误的调用。
抛出的异常未处理,Python就会显示反向跟踪,调用traceback.format_exc(),可得到其字符串形式。
将反向跟踪信息写入日志文件:
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.')
116
The traceback info was written to errorInfo.txt.
write()方法返回值是116,因为文件写入116个字符,反向跟踪文本被写入errorInfo.txt.
Traceback (most recent call last):
File “<pyshell#28>”, line 2, in
Exception: This is the error message.
10.3 断言(assert)
断言,检查代码是否心智正常,确保其没有做明显错误的事情。
assert语句执行心智正常的检查,若失败则抛出异常。包含:
1.assert关键字;
2.条件(True\False);
3.逗号;
4.条件为False时显示的字符串。
例如:
>>>podBayDoorStatus = 'open'
>>>assert podBayDoorStatus == 'open', 'The pod bay doore 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".'
Traceback (most recent call last):
File "<pyshell#3>", line 1, in <module>
assert podBayDoorStatus == 'open', 'The pod bay doors need to be "open".'
AssertionError: The pod bay doors need to be "open".
"我断言这个条件为真,如果不为真,程序中什么地方就有一个缺陷。”
断言用于寻找错误的代码,try-except排除用户输入错误。
10.3.1 在交通灯模拟中使用断言
market_2nd = {'ns': 'green', 'ew': 'red'} #Market街和第2街路口
mission_16th = {'ns': 'red', 'ew': 'green'} # Mission街和第16街路口
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)
在switchLights()中添加断言,确保至少有一个交通灯是红色。
--snip--
assert 'red' in 'stopligh.values(), 'Neither light is red! ' + str(stoplight)
有此断言,程序会崩溃并提示:
Traceback (most recent call last):
File "stopLight.py", line 13, in <module>
switchLights(market_2nd)
File "stopLight.py", line 11, in switchLights
assert 'red' in stoplight.values(), 'Neither light is red!' + str(stoplight)
AssertionError: Neither light is red!{'ns': 'yellow', 'ew': 'green'}
断言检查心智正常与否,使程序及早结束,省去大量调试工作。
10.3.2 禁用断言
在Python运行时传入-O选项,可以禁用断言。完成程序的编写和测试后,不再断言,以免减慢程序速度。
如何启用-O选项?
从终端窗口运行Python 时,在python 或python3 之后和.py 文件之前加上-O 开关。这将运行程序的优化版本,跳过断言检查。
10.4 日志
logging模块使创建自定义的消息记录。(记日志,可以理解程序中发生的事情,以及发生的顺序)
10.4.1 使用日志模块
import logging
logging.basicConfig(level=logging.DEBUG, format=' %(asctime)s - %(levelname)s- %(message)s') # 打印一行信息。格式在basicConfig()中指定, 包括传递给debug()的消息。
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')
输出:
stopLight.py
2020-03-16 21:27:27,860 - DEBUG- Start of program
2020-03-16 21:27:27,860 - DEBUG- Start of factorial(5%)
2020-03-16 21:27:27,861 - DEBUG- i is 0, total is 0
2020-03-16 21:27:27,861 - DEBUG- i is 1, total is 0
2020-03-16 21:27:27,861 - DEBUG- i is 2, total is 0
2020-03-16 21:27:27,861 - DEBUG- i is 3, total is 0
2020-03-16 21:27:27,861 - DEBUG- i is 4, total is 0
2020-03-16 21:27:27,861 - DEBUG- i is 5, total is 0
2020-03-16 21:27:27,861 - DEBUG- End of factorial(5%)
2020-03-16 21:27:27,861 - DEBUG- End of program
0
Process finished with exit code 0
可发现需更改为range(1, n+1)
10.4.2 不要用print()调试
print()给用户看的。logging给程序员看。
10.4.3 日志级别
向basicConfig()函数,level关键字传入不同级别的参数。
10.4.4 禁用日志
logging.disable()
10.4.5 日志记录到文件
logging.basicConfig(filename=‘myProgramlog.txt’, level=…)
10.5 IDLE的调试器
Debug->Debugger->Debug Control
勾选Stack、Locals、Source和Globals
1.Go 至终止或断点
2.Step 步入下一行(进入函数第一行)
3.Over 下一行(跨过函数)
4.Out 全速执行当前函数,至返回
5.Quit 立即终止
6.断点 set Breakpoint