Python Debug(調試)的終極指南


前言


即便您編寫了清晰可讀的代碼,即便您是很是有經驗的開發人員,奇怪的bug也不可避免地會出現,您將須要以某種方式調試它們。不少人使用一堆print語句來查看代碼中發生了什麼。這種方法遠不是理想的,有更好的方法能夠找出代碼的錯誤所在,本文將探討其中一些問題和應對方法。python




日誌是必須的


若是在編寫應用程序時沒有設置日誌記錄,那麼您最終會後悔的。應用程序中沒有任何日誌會使故障排除變得很是困難。幸運的是,在Python中,創建基本的日誌程序很是簡單:nginx


import logginglogging.basicConfig( filename='application.log', level=logging.WARNING, format= '[%(asctime)s] {%(pathname)s:%(lineno)d} %(levelname)s - %(message)s', datefmt='%H:%M:%S')
logging.error("Some serious error occurred.")logging.warning('Function you are using is deprecated.')


這就是全部你須要開始寫日誌的文件,它看起來像這樣,你能夠找到文件的路徑使用shell

logger . getloggerclass ().root.handlers[0].baseFilename):ruby


[12:52:35] {<stdin>:1} ERROR - Some serious error occurred.[12:52:35] {<stdin>:1} WARNING - Function you are using is deprecated.



__repr__ 可讀的日誌



對代碼進行簡單的改進,使其更具可調試性,能夠在類中添加__repr__方法。若是你不熟悉這個方法-它所作的只是返回一個類實例的字符串表示。使用__repr__方法的最佳實踐是輸出可用於從新建立實例的文本。例如:服務器


class Circle: def __init__(self, x, y, radius): self.x = x self.y = y self.radius = radius
def __repr__(self): return f"Rectangle({self.x}, {self.y}, {self.radius})"
...c = Circle(100, 80, 30)repr(c)# Circle(100, 80, 30)



除了__repr__,在調用print(實例)時,執行__str__方法也是一個好主意。有了這兩種方法,你能夠經過打印你的變量獲得不少信息。微信



針對字典的__missing__方法


若是出於某種緣由須要實現自定義dictionary類,那麼在嘗試訪問一些實際上不存在的密鑰時,您可能會遇到一些由keyerror引發的錯誤。爲了不在代碼中處處查看丟失了哪一個鍵(key),你能夠實現特殊的__miss__方法,每次KeyError被提出時調用。app


class MyDict(dict): def __missing__(self, key): message = f'{key} not present in the dictionary!' logging.warning(message) return message # Or raise some error instead


上面的實現很是簡單,只返回和記錄丟失鍵的消息,可是您還能夠記錄其餘有價值的信息,以便了解代碼中出現了什麼問題。less



調試 崩潰的應用程序


若是您的應用程序在您有機會了解其中發生了什麼以前就崩潰了,那麼您可能會發現這個技巧很是有用。編輯器


使用-i參數運行應用程序(python3 -i app.py)會致使程序一退出就啓動交互式shell。此時,您能夠檢查變量和函數。ide


若是這還不夠好,您能夠帶一個更強大的工具 - pdb - Python調試器。pdb有不少特性,能夠單獨寫一篇文章來講明。但這裏有一個例子和最重要的部分的綱要。讓咱們先看看崩潰腳本:


# crashing_app.pySOME_VAR = 42
class SomeError(Exception): pass
def func(): raise SomeError("Something went wrong...")
func()


如今,若是咱們用-i參數運行它,咱們就有機會調試它:


# Run crashing application~ $ python3 -i crashing_app.pyTraceback (most recent call last): File "crashing_app.py", line 9, in <module> func() File "crashing_app.py", line 7, in func raise SomeError("Something went wrong...")__main__.SomeError: Something went wrong...>>> # We are interactive shell>>> import pdb>>> pdb.pm() # start Post-Mortem debugger> .../crashing_app.py(7)func()-> raise SomeError("Something went wrong...")(Pdb) # Now we are in debugger and can poke around and run some commands:(Pdb) p SOME_VAR # Print value of variable42(Pdb) l # List surrounding code we are working with 2  3 class SomeError(Exception): 4 pass 5  6 def func(): 7 -> raise SomeError("Something went wrong...") 8  9 func()[EOF](Pdb) # Continue debugging... set breakpoints, step through the code, etc.


上面的調試會話很是簡單地展現了使用pdb能夠作什麼。程序結束後,咱們進入交互式調試會話。首先,導入pdb並啓動調試器。此時,咱們可使用全部pdb命令。做爲上面的示例,咱們使用p命令打印變量,使用l命令列出代碼。大部分時間你可能會想要設置斷點,能夠與b LINE_NO和運行程序,直到斷點(c),而後繼續與年代,逐頁瀏覽功能的選擇可能與w。


堆棧跟蹤


假設您的代碼是運行在遠程服務器上的Flask或Django應用程序,在那裏您沒法得到交互式調試會話。在這種狀況下,你可使用traceback和sys包來了解你的代碼中失敗的地方:


import tracebackimport sys
def func(): try: raise SomeError("Something went wrong...") except: traceback.print_exc(file=sys.stderr)


在運行時,上面的代碼將打印引起的最後一個異常。除了打印異常,您還可使用traceback包來打印stacktrace (traceback. print_stack())或提取原始堆棧幀,格式化它並進一步檢查它(traceback. format_list(traceback.extract_stack()))。


在調試期間從新加載模塊


有時,您可能在交互式shell中調試或試驗某些函數,並常常對其進行更改。爲了使運行/測試和修改的循環更容易,您能夠運行importlib.reload(模塊),以免在每次更改後從新啓動交互會話:


>>> import func from module>>> func()"This is result..."
# Make some changes to "func">>> func()"This is result..." # Outdated result>>> from importlib import reload; reload(module) # Reload "module" after changes made to "func">>> func()"New result..."


這個技巧更多的是關於效率而不是調試。可以跳過一些沒必要要的步驟,使您的工做流程更快、更高效老是很好的。通常來講,不時地從新加載模塊是一個好主意,由於它能夠幫助您避免調試已經被修改了不少次的代碼。


總結


Debug是一門藝術。


英文原文:https://towardsdatascience.com/ultimate-guide-to-python-debugging-854dea731e1b





本文分享自微信公衆號 - Python學會(gh_39aead19f756)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索