本篇博文主要記錄Python的Debug方法:print()、logging、ipdb ...
前言
Python已經學了這麼久了,你現在已經長大了,該學會自己調試代碼了!相信大家在編寫程式過程中會遇到大量的錯誤信息,我也不例外的啦~遇到這些問題該怎麼解決呢?使用最多的方法就是使用print列印中間變數了哇,關於這種方法怎麼說呢~low!!!這一節將記錄Python中一項很重要的技能:Debug(代碼調試),Here We Go!
一、代碼調試概述
1.1 概述
一個程式員在編寫項目的時候,敲代碼其實並不會占用太多的時間,占用時間的其實是敲代碼之前(整個項目的思路和框架)和敲代碼之後(調試代碼)。調試代碼這個過程是最讓人煩心的事情了,真的是煩到脫髮~於是有一項過硬的Debug技巧將會減緩掉頭髮的速度。Debug的方法有很多,最常用的就是:列印中間變數(print)、使用日誌模塊(logging)、使用代碼調試模塊(pdb或ipdb)。接下來將會一一講解
二、Debug方法一:print函數
2.1 print方法適用情景
在程式報錯或者結果與預期不符合時,在源代碼中直接使用print函數列印中間變數進行檢查。
2.2 print方法例子
1 ''' 2 從下列段落中提取出所有數字,並輸出 3 本例結果應該是:49737 4 ''' 5 import re 6 7 8 test = ''' JAKARTA, Indonesia—Flag carrier Garuda Indonesia said it is seeking to cancel an order for 49 Boeing Co. 737 MAX jets, saying passengers have lost confidence in the aircraft following two deadly crashes in recent months. ''' 9 10 pattern = re.compile('\d') 11 12 result = re.findall(pattern, test) 13 14 print(result[0] + result[1]) 15 16 # 運行結果: 17 49
題目要求輸出49737,但是自己的程式卻只輸出了49,這是怎麼回事呢?
那就使用print列印一下result這個變數的內容哇,於是,在第13行代碼中加入 print(result) :
1 ''' 2 從下列段落中提取出所有數字,並輸出 3 本例結果應該是:49737 4 ''' 5 import re 6 7 8 test = ''' JAKARTA, Indonesia—Flag carrier Garuda Indonesia said it is seeking to cancel an order for 49 Boeing Co. 737 MAX jets, saying passengers have lost confidence in the aircraft following two deadly crashes in recent months. ''' 9 10 pattern = re.compile('\d') 11 12 result = re.findall(pattern, test) 13 print(result) 14 15 print(result[0] + result[1])
# 運行結果: ['4', '9', '7', '3', '7'] 49
這時就會發現原來是result變數有誤,預期result效果為['49', '737']
於是回過頭去檢查pattern,發現是pattern的鍋,應將pattern改為:
pattern = re.compile('\d+')
於是整個程式變為:
3 ''' 4 從下列段落中提取出所有數字,並輸出 5 本例結果應該是:49737 6 ''' 7 import re 8 9 10 test = ''' JAKARTA, Indonesia—Flag carrier Garuda Indonesia said it is seeking to cancel an order for 49 Boeing Co. 737 MAX jets, saying passengers have lost confidence in the aircraft following two deadly crashes in recent months. ''' 11 12 # pattern = re.compile('\d') 13 pattern = re.compile('\d+') 14 15 result = re.findall(pattern, test) 16 print(result) 17 18 print(result[0] + result[1])
# 運行結果: ['49', '737'] 49737
結果與預期一樣,最後再將 print(result) 這一行刪除,整個程式就完工了。
『防抄襲:讀者請忽略這段文字,文章作者是博客園的MinuteSheep』
2.3 print方法優缺點
優點:
- 理解和操作方便簡單,上手難度低
- 在編寫程式中可以一步一步檢查中間變數是否符合預期
缺點:
- 直接入侵源代碼,如果操作失誤可能會造成不可輓救的後果
- 調試完成後需要將這些print行刪除掉或者註釋掉,否則會造成整個程式運行結果複雜,同時太多print函數的出現會嚴重拖慢運行速度
三、Debug方法二:logging模塊
3.1 日誌概述
日誌是個什麼鬼呢?感覺好像日記的樣子哎~日誌其實和日記是有很大差別的,日誌是用來追蹤程式運行過程中發生的事情,將這些事情按照一定的格式寫入特定的文件中,以後可以通過分析日誌,讓管理者更加方便地瞭解整個程式的的運行情況,尤其是瞭解到程式的健康狀態(優秀的日誌分析者甚至可以通過日誌分析出開發者的操作習慣和興趣愛好),最後根據這些結果為程式打上合適的補丁。
3.2 日誌作用
- 代碼調試
- 記錄程式的運行狀況
- 為程式打補丁提供支撐
3.3 日誌等級
在講Python日誌方法之前,先來瞭解一下日誌中最重要的等級制度:
通常日誌分為5個等級:DEBUG, INFO, WARNING, ERROR, CRITICAL
日常編程過程中應該見過WARNING和ERROR吧,一個是警告,一個是錯誤
從這些單詞的英文釋義就可以知道每個等級的權重了,DEBUG等級最小,CRITICAL等級最大
還有更詳細的等級分法:DEBUG, INFO, NOTICE, WARNING, ERROR, CRITICAL, ALERT, EMERGENCY
3.4 logging模塊簡介
Python中用來記錄日誌的模塊為:logging,這是一個內置標準庫
logging模塊的日誌等級有5個:DEBUG, INFO, WARNING, ERROR, CRITICAL,另外,logging模塊支持用戶自定義其他等級,但這並不推薦,因為自定義的等級通常會造成等級混亂,極其容易和他人開發的程式衝突。
日誌等級 等級說明
DEBUG 詳細信息,通常僅在診斷問題時才有意義
INFO 詳細程度僅次於DEBUG,確認事情按預期工作。
WARNING 表示發生了意外情況,或表明在不久的將來出現了一些問題,但該軟體仍在按預期工作。
ERROR 由於更嚴重的問題,該軟體無法執行某些功能。
CRITICAL 嚴重錯誤,表明程式本身可能無法繼續運行。
3.5 logging模塊使用方法
3.5.1 日誌基本使用方法
1 import logging 2 3 logging.debug('My level is debug') 4 logging.info('My level is info') 5 logging.warning('My level is warning') 6 logging.error('My level is error') 7 logging.critical('My level is critical')
上述代碼就是5種不同級別日誌的使用方法,運行一下看看結果如何:
# 運行結果 WARNING:root:My level is warning ERROR:root:My level is error CRITICAL:root:My level is critical
這時一定在疑惑,明明是五個等級啊,怎麼只輸出了後三個等級的內容
logging模塊雖然有5的等級,但是他的預設最小等級是WARNING,也就是說,logging模塊會自動忽略WARNING以下的等級,那怎麼才能輸出5個等級的內容呢?
需要在所有輸出語句之前配置日誌,看例:
1 import logging 2 3 logging.basicConfig(level=logging.DEBUG) # 配置日誌 4 5 logging.debug('My level is debug') 6 logging.info('My level is info') 7 logging.warning('My level is warning') 8 logging.error('My level is error') 9 logging.critical('My level is critical')
# 運行結果: DEBUG:root:My level is debug INFO:root:My level is info WARNING:root:My level is warning ERROR:root:My level is error CRITICAL:root:My level is critical
那麼問題來了, logging.basicConfig 是個什麼鬼呢?這個其實就是日誌的配置函數,可以配置日誌等級、日誌輸出文件、日誌文件的打開模式(預設是追加)、日誌格式、日期格式等,這些選項對應的參數分別為:level、filename 、filemode、format、datefmt,舉個例子:
1 import logging 2 3 logging.basicConfig(level=logging.DEBUG, filename='test.log') 4 5 logging.debug('My level is debug') 6 logging.warning('My level is warning') 7 logging.error('My level is error')
運行之後會發現屏幕並沒有日誌信息,而是在當前目錄下生成一個文件名為‘test.log’的日誌文件,打開看一下這個文件:
DEBUG:root:My level is debug WARNING:root:My level is warning ERROR:root:My level is error
可以看到已經將日誌寫入到指定文件了,這樣就可以將日誌保存下來供以後分析利用了。
有的小伙伴會說,自己能不能修改日誌的輸出格式呢?比如想要加入時間,完全沒有問題,這就要使用format參數了,關於format參數的使用,在下麵羅列了一張表格供大家參考:
1 使用格式 概述 2 %(asctime)s 日誌發生時間 3 %(created)f 日誌發生時間戳 4 %(relativeCreated)d 日誌發生時間相對於logging模塊載入時間的相對毫秒 5 %(msecs)d 日誌發生時間的毫秒部分 6 %(levelname)s 日誌發生的文字等級(debug、info、warning、error、critical) 7 %(levelno)s 日誌發生的數字等級(10、20、30、40、50) 8 %(name)s 日誌記錄器名稱(預設是root) 9 %(message)s 日誌文本內容 10 %(pathname)s 調用日誌的源代碼的絕對路徑 11 %(filename)s pathname的文件名部分,包括尾碼名 12 %(module)s filename的文件名部分,不包含尾碼名 13 %(lineno)d 調用日誌的源代碼的行數 14 %(funcName)s 調用日誌的函數名 15 %(process)d 進程號 16 %(prscessName)s 進程名 17 %(thread)d 線程號 18 %(thread)s 線程名
舉個例子:
1 import logging 2 3 LOG_FORMAT = '%(asctime)s - %(levelname)s - %(name)s - %(message)s' 4 logging.basicConfig(level=logging.DEBUG, filename='test.log', format = LOG_FORMAT) 5 6 logging.debug('My level is debug') 7 logging.warning('My level is warning') 8 logging.error('My level is error')
查看‘test.log’:
DEBUG:root:My level is debug WARNING:root:My level is warning ERROR:root:My level is error 2019-03-24 10:52:46,093 - DEBUG - root - My level is debug 2019-03-24 10:52:46,094 - WARNING - root - My level is warning 2019-03-24 10:52:46,094 - ERROR - root - My level is error
可以看到日誌輸出格式明顯發生了改變
註意:打開文件模式預設為追加
如果想要改變時間的輸出格式,需要使用datefmt參數,要註意datefmt參數要在format參數里有時間的前提下才會生效,這裡就不在舉例了,關於時間的格式可以參考time模塊時的講解
3.5.2 日誌高級使用方法
以後再介紹哇,基本的使用方法已經可以滿足使用了,高級使用方法比較複雜,以後再更新
四、Debug方法三:pdb模塊和ipdb模塊
4.1 pdb和ipdb概述
pdb是Python內置的Debug模塊,但是其功能不夠強大,於是便有了第三方模塊ipdb的出現;它們兩個的關係就好像python和ipython的關係。
ipdb調試代碼是比print函數更加高級和靈活的方式,應當熟練應用ipdb的使用方式,並且取代print這種low方法