python 调试大法-大笨蛋的笔记

说在前面html

  我以为没有什么错误是调试器没法解决的,若是没有,那我再说一遍,若是有,那当我没说python

1、抛出异常程序员

  能够经过 raise 语句抛出异常,使程序在咱们已经知道的缺陷处停下,并进入到 except 语句shell

  

  raise句法:编程

    raise关键字网络

    调用的异常函数名 ValueError (这个函数必须是异常类或一个实例)数据结构

    传递给 ValueError 的字符串,包含有用的出错信息dom

>>> raise ValueError('This is a error message')
Traceback (most recent call last):
  File "<pyshell#24>", line 1, in <module>
    raise ValueError('This is a error message')
ValueError: This is a error message

  而后使用 try...except 语句来对抛出的异常作处理编辑器

  一般咱们在函数自己中抛出异常,而后在调用该函数的地方使用 try...except 语句处理异常函数

#定义一个简单的int类型的加法器
def calculator(num1,num2):
    if isintance(num1,int)and isintance(num2,int):
        raise Exception('Symbol must be a int type number.')
    return num1+num2

print('please enter two number:')
num1=input()
num2=input()
#在调用函数的地方使用try语句
try:
    print(calculator(num1,num2))
except Exception as err:
    print('发生了一个错误:'+str(err))
#另外一种使用状况
try:
            print(key)
            return self[key]
        except KeyError:#若是在上面碰见了keyError
            raise AttributeError(r"'%s' don't have attribute '%s'"%#就抛出这个AttributeError类型的错误,顺序别弄错
(self.__class__.name,key))

  

  注意上面的 as 语句取得 str ,若是不取也是能够的

  

  运行示例:

 RESTART: C:/Users/Administrator.SC-201605202132/AppData/Local/Programs/Python/Python37/boxPrint.py 
please enter two number:
1
s
发生了一个错误:name 'isintance' is not defined
>>> 

 

2、取的反向跟踪的字符串

 

  当程序运行出现错误时,python会生成一些错误信息,这些错误信息被称为“反向跟踪”,它包含了出错信息、致使该错误的代码行号,和致使 该错误的函数调用 的 序列,这个序列被称为调用栈

  只要抛出的异常没有被处理,python就会显示反向跟踪

  如下面程序来展现咱们对反向跟踪的解读

  

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

spam()

  这就是反向跟踪:

Traceback (most recent call last):
  File "C:/Users/Administrator.SC-201605202132/AppData/Local/Programs/Python/Python37/errorExample.py", line 6, in <module>
    spam()
  File "C:/Users/Administrator.SC-201605202132/AppData/Local/Programs/Python/Python37/errorExample.py", line 2, in spam
    bacon()
  File "C:/Users/Administrator.SC-201605202132/AppData/Local/Programs/Python/Python37/errorExample.py", line 4, in bacon
    raise Exception('This is the error message')
Exception: This is the error message

  咱们应该从下往上阅读方向跟踪,经过反向跟踪咱们能够知道,这个错误发生在第5行,在bacon函数中;此次特定的bacon调用发生在第2行,spam函数中,而spam函数又是在第6行被调用的。这样,在从多个位置调用函数的程序中,调用栈就能帮助你肯定那次调用致使了错误。

 

调用 traceback.format_exc() 获得反向跟踪的字符串形式

 

  前面说过,若是抛出的异常没有被处理,python才会显示反向跟踪。假如咱们既想用except处理错误,又想要得到出错信息,就能够用这个函数,须要导入 traceback 模块

  例如,咱们能够在程序出现错误时还能继续运行,同时把错误信息记录到日志中。在程序结束后调试程序时,咱们就根据日志里记录的信息去调试

  

>>> import traceback
>>> try:
	raise Exception('This is a error message')
except:
	errorFile=open('errorInfo.txt','w')
	errorFile.write(traceback.format_exc())  #使用tracback.format_exc()得到反向跟踪的字符串形式
	errorFile.close()
	print('The traceback info was written to errorInfo.txt')

	
112    #返回的是写入的字符个数
The traceback info was written to errorInfo.txt
>>> 

  errorInfo.txt的内容:

Traceback (most recent call last):
  File "<pyshell#8>", line 2, in <module>
Exception: This is a error message

  

3、断言assert语句

 

  举一个例子。你从学校毕业之后,好久都没有找到工做,有一天你找了一个兼职:宝石大管家。小孩须要拿着与他们身份匹配的标识才能在别处领到宝石,这个标识在你这里领取,你工做作得不错,才作了五分钟就被老板任命为了区域经理,你觉你年纪轻轻就已经成为了二龙山云霄飞车街区的揸Fit人、而且一手创建了二龙山游乐场宝石交易的游戏法则,以为人生巅峰也不过如此,可是,沉迷于自我陶醉的你根本不知道,你将一个错误的标识给了一个小朋友,致使他没有领到宝石。结果他叫他哥哥来打你了一顿。而后你老板以为你辜负了他对你的栽培,而后一气之下把你开了,工资固然没有结。最惨的是,你的衣服丢了,当时你为了用肚脐眼上的伤疤吓唬他就把衣服脱了,结果他居然也有一样的伤疤,而后又被他打了一顿,而后,你的衣服就丢了。你知道这是你最宝贵的财富,由于这是当年女神赠你的礼物,你永远也忘不了毕业那天,在你的寝室楼下,他轻轻的把袋子递给了你,那天大家说了不少,他说感谢你四年来对他的照顾,可是他妈妈不让他谈恋爱,因此让你再等等,你和他一直聊到晚上10点,只为了能当面向他说一句晚安,他很欣赏你的执着,离别之际对你许下了一个承诺:她说假若有一天这件衣服变成了绿色,他必定和你结婚。你知道,这下确定没有但愿了。不只失去了工做,你失去了爱情。你觉得丢了衣服,就再也没机会和他结婚了,万万没想到,最后大家仍是成为了夫妻。那天你回来之后就去了网吧,看见旁边的人在写代码,他周围散落的零食包装表明着富有,这一切都被你看在眼里,你知道你看到了但愿,而后你就开始学编程了,因为你过人的天赋,没出几十年你就本身创办了一家公司,和阿里啪啪,中国移不动等大公司都创建了不一样程度的合做关系,且业务往来十分密切,身边的人都夸你有出息,只是在深夜的时候,你经常想起当年的那个他,你祈求老天再给你一次机会,终于有一天,你qq收到了他的信息,她说要来找你,你在城市最有档次的地方约她吃饭,他一眼就认出来了你,你很开心,你以为他一点都没变,仍是原来的样子,他没有问你衣服的事情,只是不停的向你道歉说是手误当时才把你删了,其实他这些年一直在找你,此次找到你了,就是要和你结婚,你十分激动,可是你强忍着激动的心情,劝他在考虑考虑,他摇了摇头,从他眼神里流露出来的坚决瞬间击垮了你,你再也控制不了本身了,你拿出了那次作兼职留下的宝石钻戒,你一直把它带在身上,就是等着机会到来,他想都没想就一口答应了你的求婚。看到他对你如此信赖,你暗暗发誓必定要用所有的智商去爱她,晚上他非要枕着你的胳膊睡觉,你虽然觉的不舒服但仍是让他枕了一晚上,你作了一个梦,梦见大家有了本身的孩子,那件衣服也被你找到了 衣服上还写着“前方高能”几个字,这是你睡得最舒服的一个晚上,你早早就醒来了,发现他也已经起来了,就在床边上坐着,但令你不解的是,看到你睁开了眼睛,他的表情突然很激动,sua的一声就哭了,等他冷静下来你才知道。原来,你应经昏迷了8年了,8年前,你去买早餐就再也没有回来,你出了车祸,昏迷了8年,留下他和他腹中的孩子。他说这些年他历来没有想过放弃你,他对你的爱帮助他克服了许多困难。现在你醒了,他终于成功了,他高兴的留下了激动的泪水,你也很开心。因而今后之后,大家一家三口过上了幸福的生活。

 

  “断言”在这个工做流程当中,就是用来检查 你是否把牌发对了 的一个机制。为了不这样的状况,咱们就添加“断言”来检查。

 

    assert语句包含:

  assert关键字、要判断的条件、逗号、条件为False时显示的字符串

>>> podBayDoorStatus='open'  #吊舱门的状态
>>> assert podBayDoorStatus=='open','podBayDoorStatus须要设置为open'
#这里结果没有错
>>> podBayDoorStatus='other content'
>>> assert podBayDoorStatus=='open','podBayDoorStatus须要设置为open'
#这里结果出错了
Traceback (most recent call last):
  File "<pyshell#13>", line 1, in <module>
    assert podBayDoorStatus=='open','podBayDoorStatus须要设置为open'
AssertionError: podBayDoorStatus须要设置为open
>>> 

  咱们在程序中为某个变量赋值后,基于 这个变量是这个值 的假定,咱们可能写下了大量的代码,即这些代码依赖这个值,才能正确工做。说以咱们添加一个断言,确保假定的变量值是对的。

  对于这种状况,咱们使用assert让程序当即崩溃就,以减小寻找缺陷的时间,咱们不该用 try except 抛出异常,由于这是程序员的错误,而不是用户的错误,对于那些能够恢复的错误(如文件没有找到,用户输入了无效的数据)则应该用抛出异常来处理

  

  在交通灯模拟中使用断言

  

  你在编写一个交通讯号灯的模拟程序。表明路口信号灯的数据结构是一个字典:

market_2nd={'ns':'green','ew':'red'}#ns南北向,ew东西向

  你但愿编写一个函数 switchLight() ,他接受一个路口字典做为参数,并切换红路灯

  你可能认为 switchLight() 只要将每一种灯按顺序切换到下一种颜色: ‘green‘ 值应该切换到 'yellow' , 'yellow' 应该切换到 'red' , 'red' 应该切换到 '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'

  这样的运行结果:

>>> 
 RESTART: C:\Users\Administrator.SC-201605202132\AppData\Local\Programs\Python\Python37\forTest.py 
{'ns': 'yellow', 'ew': 'green'}
{'ns': 'red', 'ew': 'yellow'}
{'ns': 'green', 'ew': 'red'}

  

  你应该发现第一次的输出是错误的,由于南北向和东西向总应该有一个是红色的,若是不是,那么就会出现汽车相撞,为了不这样的缺陷出现,你应该添加断言

market_2nd={'ns':'green','ew':'red'}#ns南北向,ew东西向

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'
    assert 'red' in stoplight.values(),'交通灯都不是红色的'+str(stoplight)        #在函数里面添加断言
switchLights(market_2nd)
print(market_2nd)
switchLights(market_2nd)
print(market_2nd)
switchLights(market_2nd)
print(market_2nd)

  

  假如你没有看出来这个代码有问题,而后也没有使用断言,当你从运行结果发现问题时,或许要好多时间才能发现问题出如今 stwitchLight 函数中

 

禁用断言

  当咱们开发测试的时候,咱们可使用断言来帮助咱们更早的发现错误,可是程序交付的时候应该是没有缺陷的,这时就不在须要断言了,咱们能够在运行python时传入-O选项来禁用断言

  须要从终端窗口运行程序时使用 >>>从终端运行程序<<<

  

 

 

4、日志

  记日志是一种很好的方式,让咱们能够理解程序中发生的事,以及事情发生的顺序。python中的 logging 模块让你能很容易的建立自定义的消息记录。这些日志消息列出了你指定的 任何变量 当时的值。缺失日志消息代表有一部分代码被跳过了,从未执行

  

  4.1使用日志模块

  

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

  咱们使用 logging.debug('string') 来打印日志信息,这个 debug() 函数会调用 basicConfig ,因此咱们第二行是指定打印信息的格式

  python记录一个时间的日志时,他会建立一个 logRecord 对象,保存关于该事件的信息。

   logging.debug() 调用不只打印出了咱们传递给他的信息,并且包含时间戳和一个单词DEBUG

  

  咱们如下面的程序为例,展现使用日志来调试程序的大体过程

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))  #这里的两个%是什么意思?或许是匹配basicConfig()里format里的后两个参数?
    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')

  运行结果:

 RESTART: C:/Users/Administrator.SC-201605202132/AppData/Local/Programs/Python/Python37/facatorialLog.py 
 2019-03-06 17:39:10,889 - DEBUG - Start of program
 2019-03-06 17:39:10,938 - DEBUG - Start of factorial(5%)
 2019-03-06 17:39:10,973 - DEBUG - i is 0, total is 0
 2019-03-06 17:39:11,001 - DEBUG - i is 1, total is 0
 2019-03-06 17:39:11,030 - DEBUG - i is 2, total is 0
 2019-03-06 17:39:11,058 - DEBUG - i is 3, total is 0
 2019-03-06 17:39:11,083 - DEBUG - i is 4, total is 0
 2019-03-06 17:39:11,108 - DEBUG - i is 5, total is 0
 2019-03-06 17:39:11,132 - DEBUG - End of factorial(5%)
0
 2019-03-06 17:39:11,187 - DEBUG - End of program

  从里面咱们能够看到i是从0开始的,这就致使了total变量老是0,固然结果也是0,知道了这些,咱们就能够对程序进行改动

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))
    #或许是匹配basicConfig()里format里的后两个参数?
    total=1
    for i in range(1,n+1):    #改动在这里
      
---snip--

 

  4.2日志级别

   这个级别是全局的

  “日志级别”提供了一种方式,按重要性把日志消息分为了下面5类。这些级别只是一种建议,在工做中,仍是有咱们本身来为日志消息指定类型。就像上面,咱们也能够不用 logging.debug() 而选用其余四种  

python中的日志级别
级别(上面的是最小的) 日志函数 描述
DEBUG logging.debug() 最低级别。用于小细节。一般你只有在诊断问题时才须要
INFO logging.info() 用于记录程序中通常事件的信息,或者是用来确认工做正常
WARNING logging.warning() 用于表示可能的问题,这些问题不会阻止程序的工做,但未来可能会
ERROR logging.error() 用于记录错误,它致使程序作某事失败
CRITICAL logging.critical() 最高级别。用于表示致命的错误,它致使或将要致使程序彻底中止工做

  他们显示的格式并区别

>>> import logging
>>> logging.basicConfig(level=logging.DEBUG,format=' %(asctime)s - %(levelname)s - %(message)s')
>>> logging.debug('Some debugging details')
 2019-03-06 18:13:44,829 - DEBUG - Some debugging details
>>> logging.info('The logging is working')
 2019-03-06 18:13:59,984 - INFO - The logging is working
>>> logging.critical('The program is unable to recover!')
 2019-03-06 18:14:34,237 - CRITICAL - The program is unable to recover!
>>> 

  

  “日志级别”的好处

  “日志级别”的好处在于,你能够改变想看到的 日志消息 的优先级。这经过 basicConfig() 函数的level关键字参数来指定, level='logging.DEBUG' 时会显示全部的日志级别消息, level='logging.ERROR' 时只会显示级别大于等于ERROR的日志消息

当咱们开发了更多程序后,咱们可能只会对错误感兴趣,这种状况,就能够经过上面的level参数来设定咱们想看到的级别

 

  4.3禁用日志

 

   logging.disable() 函数接受一个日志级别,它会禁止该级别和更低级别的全部日志消息,注意这个参数的书写正确

>>> import logging
>>> logging.basicConfig(level=logging.DEBUG,format=' %(asctime)s - %(levelname)s - %(message)s')
>>> logging.critical('The program is unable to recover!')
 2019-03-06 18:14:34,237 - CRITICAL - The program is unable to recover!
>>> logging.disable(logging.CRITICAL)
>>> logging.critical('The program is unable to recover!')#因为上面的禁用这个就不显示了
>>> 

  咱们应该吧 logging.disable() 写在程序中接近 import logging 代码行的位置

 

  4.4将日志记录到文件

 

   logging.basicConfig() 函数接受 filename 关键字参数,日志消息将被保存到 myProgramLog.txt 文件中,而不会在输出在屏幕上

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

 

  4.5 basicConfig 的参数及 logging 模块定义的格式字符串字段

 

参数名称 描述
filename 指定日志输出目标文件的文件名,指定该设置项后日志信心就不会被输出到控制台了
filemode 指定日志文件的打开模式,默认为'a'。须要注意的是,该选项要在filename指定时才有效
format 指定日志格式字符串,即指定日志输出时所包含的字段信息以及它们的顺序。logging模块定义的格式字段下面会列出。
datefmt 指定日期/时间格式。须要注意的是,该选项要在format中包含时间字段%(asctime)s时才有效
level 指定日志器的日志级别
stream 指定日志输出目标stream,如sys.stdout、sys.stderr以及网络stream。须要说明的是,stream和filename不能同时提供,不然会引起 ValueError异常
style Python 3.2中新添加的配置项。指定format格式字符串的风格,可取值为'%'、'{'和'$',默认为'%'
handlers Python 3.3中新添加的配置项。该选项若是被指定,它应该是一个建立了多个Handler的可迭代对象,这些handler将会被添加到root logger。须要说明的是:filename、stream和handlers这三个配置项只能有一个存在,不能同时出现2个或3个,不然会引起ValueError异常。

  

  日志的更多信息:参见

 

5、IDLE的调试器  

 

  "调试器"是IDLE的一项功能,他可让你每次执行一行代码,并让你清除的查看当前时刻全部变量的值,对于你弄明白程序的问题颇有帮助,经过在交互窗口中点击 Debug>Debugger 来打开 调试控制窗口

 

  5.1窗口上的信息

  调试的时候不要把把断点打到相似while语句上,由于while这样的语句只执行一次,执行屡次的是里面包裹的代码,因此单步跳出或者继续的时候就至关于继续执行到这个while语句,结束了要想一次让单步跳出或者继续达到一次执行一轮 while里面代码的效果,就把断点打到while里面

 

  打开调试窗口后,只要你运行程序调试器就会在第一条指令执行前暂停执行,并显示下面的信息:

  将要执行的代码行;全部局部变量其其值得列表;全部全局变量及其值的列表

  你会发现这里面有多你没有定义的变量,如 __builtins__ 、 __doc__ 、 __file__ ,等等。它们是python在运行程序时,自动设置的变量。这些变量表明的含义我如今也不知道。咱们能够只关注那些咱们定义的变量。

  程序将保持暂停,知道咱们按下调试窗口的5个按钮中的一个:GO、Step、Over、Out、Quit

  Go

    点击Go按钮将致使程序正常执行至终止,或到达一个“断点”(断点稍后会说)。换句话说,若是你完成了调试,但愿程序正常继续,就点击Go按钮

  Step

    Step按钮将致使程序执行下一行代码,而后再次暂停。若是下一行代码是一个函数调用,调试器就会“步入”那个函数,调到该函数的第一行。

  Over

    Over按钮将执行下一行代码,与Step按钮相似。可是若是下一行代码是一个函数调用,Over按钮将“跨越”该函数的代码,调试器将在该函数返回后暂停。例如,下一行代码是 print() 调用,而显然咱们不关注 print() 这个函数的代码是怎样的工做的,只但愿传递给它的字符串打印出来,这时咱们就可使用Over按钮

  Out

    Out按钮将致使调试器全速执行代码行,直到它从当前函数返回。若是你用Step按钮进入了一个函数,如今想要让这个函数全速执行,直到这个函数结束,那么就可使用Out按钮,让他从当前函数调用中“走出来”

  Quit

    Quit按钮将立刻终止该程序,不会执行下面的代码,记住是终止程序,不是终止调试

 

5.2关闭调试器

 

  和打开的操做同样,从交互式窗口点击 Debug>Debugger 就会关闭

 

5.3断点

  “断点”能够设置在特定的代码行上,当使用调试器开始调试程序时,按下GO按钮并不会结束程序了,而是会到达断点里暂停。

  咱们能够在编辑器里在要设定断点的行右击鼠标,选择 Set Breakpoint ,就在当前行设置了断点,而且会以亮黄色显示,此次咱们打开调试器后,再运行程序后按GO按钮就会在这一行中止,当咱们要清除断点时,须要在当前行右击鼠标,选择 clear Breakpoint 

当咱们想要知道for循环中某一轮中的变量值,咱们就能够在那一行设置断点,而不是频繁的点击Over按钮

import random
mark=0
for i in range(1,1000):
    s=random.randint(0,2)
    if s==1:
        mark+=1
#咱们查看循环到i=500时的mark值就能够在下面设置断点    
    if i==500:
       print('halfway done')    #设置这里为断点,而不要在上一行里设置,由于他是个判断,每一轮都会运行
print(mark)

  

  断言、异常、日志和调试器,都是在程序中发现错误和预防缺陷的有用工具。用python的断言,是检查本身有没有犯错的好方式。若是必要的条件被咱们搞错了,他将会早早的给出警告。断言所针对的错误,是程序不该该尝试恢复的,而是应该让程序立马失败

  异常能够由 try...except 语句捕捉和处理。 logging 模块是一种很好的方式,能够在运行时查看代码的内部,他比使用 pring() 语句要好不少,由于他有不一样的日志级别,并能写入日志文件。

  调试器让你每次单步执行一行代码。或者能够用正常的速度运行程序,并让调试器停在你设置的断点的代码行上。利用调试器,你能够看到程序在运行期间,任什么时候候全部变量的值。

相关文章
相关标签/搜索