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類型的錯誤,順序別弄錯<br>(self.__class__.name,key))

注意上面的 as 語句取得 str ,若是不取也是能夠的
  
運行示例:

RESTART: C:/Users/Administrator.SC-201605202132/AppData/Local/Programs/Python/Python37/boxPrint.py 
please enter two number:
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--

PS:遇到問題沒人解答?須要Python學習資料?能夠加點擊下方連接自行獲取
note.youdao.com/noteshare?id=2dce86d0c2588ae7c0a88bee34324d76

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() 語句要好不少,由於他有不一樣的日誌級別,並能寫入日誌文件。

調試器讓你每次單步執行一行代碼。或者能夠用正常的速度運行程序,並讓調試器停在你設置的斷點的代碼行上。利用調試器,你能夠看到程序在運行期間,任什麼時候候全部變量的值。
轉自:http://www.javashuo.com/article/p-tercwrey-dx.html

相關文章
相關標籤/搜索