Python之第十章 调试

一、抛出异常

抛出异常:发现代码出现异常,停止运行,转而运行except中的语句。

raise用在函数中。

try和except语句调用函数的代码中。

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(symbol * width)
 for i in range(height - 2):
 print(symbol + (' ' * (width - 2)) + symbol)
 print(symbol * width)
for sym, w, h in (('*', 4, 4), ('O', 20, 5), ('x', 1, 3), ('ZZ', 3, 3)):
 try:
 boxPrint(sym, w, h)
except Exception as err:
print('An exception happened: ' + str(err))

二、取得反向跟踪的字符串

反向跟踪:Python遇到错误,就会生成一些错误信息,称为‘反向跟踪’

调用栈:反向跟踪包含了出错消息、导致该错误的代码行号,一级导致该错误的函数调用序列。

def spam():
    bacon()
def bacon():
    raise Exception('This is the error message')

spam()

Traceback (most recent call last):
  File "D:\shy\testing.py", line 6, in <module>
    spam()
  File "D:\shy\testing.py", line 2, in spam
    bacon()
  File "D:\shy\testing.py", line 4, in bacon
    raise Exception('This is the error message')
Exception: This is the error message
[Finished in 201ms]

 traceback.format_exc()方法,可以得到报错形式的字符串形式。

只要抛出的异常没有被处理,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.')

 三、断言

“断言”是一个心智正常的检查,确保代码没有做什么明显错误的事情。这些心智正常的检查由 assert 语句执行。如果检查失败,就会抛出异常。在代码中, assert 语句包含以下部分:
assert 关键字;
条件(即求值为 True False 的表达式);
逗号;
当条件为 False 时显示的字符串。

 我将变量shy设置为stronger,然后添加了一个断言,确保shy变量一直是stronger,后面又不小心给变量shy赋了其他值,再使用这个断言进行判断,就可以发现哪里地方出错了。

1、在交通灯模拟中使用断言

模拟一下信号灯,十字路口,ns代表南北方向,初始灯为绿灯,ew代表东西,方向,初始为红灯。

定义一个函数: 转换信号灯颜色,然后断言一下,必须有一个路口的灯为红色,这个时候就报错了,提醒我们代码会有一种情况是两个路口的灯都不是红色,这就容易出事了。

crossroad={'ns':'green','ew':'red'}

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(crossroad)

这里最重要的一行就是最后一行AssertionError的报错信息,它告诉我们会有一种情况,两个路口都不是红灯。

2、禁用断言

在运行Python时传入-O选项,可以禁用断言。

四、日志

之前编写代码的时候,我们经常使用print()函数输出我们想要的代码运行结果,来判断代码是否出错,但是print()会将我们自己定义的调式信息也混杂在程序运行结果当中。这个时候就可以使用Python中的logging模块,创建自定义的消息记录。

1、使用日志模块

在程序顶部加上如下代码:

import logging
logging.basicConfig(level=logging.DEBUG, format=' %(asctime)s - %(levelname)s
- %(message)s')
Python 记录一个事件的日志时, 它会创建一个 LogRecord 对象,保存关于该事件的信息。 logging 模块的函数让你 指定想看到的这个 LogRecord 对象的细节,以及希望的细节展示方式。

编写一个求阶乘的代码,看一下效果:

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

 打印日志信息时,使用 logging.debug() 函数。这个 debug() 函数将调用 basicConfig(),打印一行信息。这行信息的格式是我们在 basicConfig()函数中指定的,并且包括我们传递给 debug() 的消息

2、不要用print()调式

输入 import logging logging.basicConfig level=logging.DEBUG, format='%
(asctime)s - %(levelname)s - %(message)s' )有一点不方便。你可能想使用 print() 调用
代替,但不要屈服于这种诱惑!在调试完成后,你需要花很多时间,从代码中清除每
条日志消息的 print() 调用。你甚至有可能不小心删除一些 print() 调用,而它们不是用
来产生日志消息的。日志消息的好处在于,你可以随心所欲地在程序中想加多少就加
多少,稍后只要加入一次 logging.disable logging.CRITICAL )调用,就可以禁止日
志。不像 print() logging 模块使得显示和隐藏日志信息之间的切换变得很容易。

3、日志级别

 五种日志级别按从低到高排序:

DEBUG < INFO < WARNING < ERROR < CRITICAL

日志消息作为一个字符串,传递给这些函数。日志级别是一种建议。归根到底, 还是由你来决定日志消息属于哪一种类型。
>>> import logging
>>> logging.basicConfig(level=logging.DEBUG, format=' %(asctime)s -
%(levelname)s - %(message)s')
>>> logging.debug('Some debugging details.')
2015-05-18 19:04:26,901 - DEBUG - Some debugging details.
>>> logging.info('The logging module is working.')
2015-05-18 19:04:35,569 - INFO - The logging module is working.
>>> logging.warning('An error message is about to be logged.')
2015-05-18 19:04:56,843 - WARNING - An error message is about to be logged.
>>> logging.error('An error has occurred.')
2015-05-18 19:05:07,737 - ERROR - An error has occurred.
>>> logging.critical('The program is unable to recover!')
2015-05-18 19:05:45,794 - CRITICAL - The program is unable to recover!

4、禁用日志

>>> import logging
>>> logging.basicConfig(level=logging.INFO, format=' %(asctime)s -
%(levelname)s - %(message)s')
>>> logging.critical('Critical error! Critical error!')
2015-05-22 11:10:48,054 - CRITICAL - Critical error! Critical error!
>>> logging.disable(logging.CRITICAL)
>>> logging.critical('Critical error! Critical error!')
>>> logging.error('Error! Error!')

logging.disable()将禁用它之后的所有消息,所以它之后的logging.critical()、logging.error()都不会输出任何消息了。

5、将日志记录到文件

import logging
logging.basicConfig(filename='myProgramLog.txt', level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s')

五、IDLE的调试器

windows在Python安装目录下,双击下图该文件,打开idle

 

 编写一个程序来试用下idle的调试器

print('enter the first number to add:')
first=input()
print('enter the second number to add:')
second=input()
print('enter the third number to add:')
third=input()
print('The sum is' + first + second + third)

运行一下发现程序出错:

现在就使用idle的调试器,来调试该程序:

 

 点击over,程序执行到下一行 ,用over可以不用进入到print()函数的代码中。

继续点击over,执行input()函数调用,当我们还没有输入东西的时候,调式窗口中的按钮将被禁用。

 输入5,并按回车,调试窗口按钮将重新启用。

 重复,over,输入数字,三次。

 可以看到,在全局变量的部分,第一、二、三个变量设置为字符串,而不是整型。当最后一行执行时,他们不是相加,而是拼接。

用调试器单步执行程序很有用,但也可能很慢。你常常希望程序正常运行,直到它到达特定的代码行。你可以使用断点,让调试器做到这一点

7、断点

“断点”可以设置在特定的代码行上,当程序执行到达该行时,它迫使调试器暂停。

编写演示程序:投掷1000次硬币,人头向上的次数。

import random
heads = 0
for i in range(1,1001):
    if random.randint(0,1)==1:
        heads=heads+1
    if i==500:
        print('halfway done')
print('heads came up ' + str(heads) +' times.')

要是用调试器运行这个程序,就必须点击几千次over按钮,程序才能结束。

如果只对程序运行到一半时heads的值感兴趣,可以在代码print('halfway done!')上设置断点。

要设置断点,在文件编辑器中该 行代码上点击右键,并选择 Set Breakpoint ,如

 不能在if语句上设置断点,因为if语句也会在每次循环的时候执行。

变黄色断点就设置好了

点over一次一次的太慢,

 可以直接点go,程序执行到断点处,可以看到运行到500次时,人头向上的次数有277次

 如果希望清除断点,在文件编辑器中该行代码上点击右键,并从菜单中选择 Clear Breakpoint。黄色高亮消失,以后调试器将不会在该行代码上中断。

六、小结

    断言、异常、日志和调试器,都是在程序中发现和预防缺陷的有用工具。
    用 Python 语句实现的断言,是实现心智正常检查的好方式。如果必要的条件没有保持为 True ,它将尽早给出警告。断言所针对的错误,是程序不应该尝试恢复的,而是应该快速失败。否则,你应该抛出异常。
    异常可以由 try except 语句捕捉和处理。 logging 模块是一种很好的方式,可以在运行时查看代码的内部,它比使用 print() 函数要方便得多,因为它有不同的日志级别,并能将日志写入文本文件。
调试器让你每次单步执行一行代码。或者,可以用正常速度运行程序,并让调试器暂停在设置了断点的代码行上。利用调试器,你可以看到程序在运行期间,任何时候所有变量的值。这些调试工具和技术将帮助你编写正确工作的程序。不小心在代码中引入缺陷,这是不可避免的,不论你有多少年的编码经验

七、习题

1 .写一条 assert 语句,如果变量 spam 是一个小于 10 的整数,就触发AssertionError

2.编写一条 assert 语句,如果 eggs bacon 包含的字符串彼此相同,而且不 论大小写如何,就触发 AssertionError(也就是说,'hello' 'hello' 被认为相同, 'goodbye' 和 'GOODbye' 也被认为相同)。

assert(eggs.lower()!=bacon.lower(),'ther are must same')
3 .编写一条 assert 语句,总是触发 AssertionError
assert(False,'this is always triggers')
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值