01.18 揭祕 IPython 的 5 種最佳調試方法

一個好的集成開發環境(IDE)附帶的調試器是開發人員能夠擁有的最強大的工具之一,但並不是每個人都在使用一個帶有很棒代碼調試器的集成發環境(IDE)。作為程序員,在調試 Python 代碼時,你覺得最好用的調試器有哪些?如果沒有的話,不妨試試使用 IPython 作為調試器吧。

揭秘 IPython 的 5 种最佳调试方法

作者 | switowski

Tenderlove(Ruby和Rails的核心開發人員之一)寫了一篇很棒的文章,叫做“I am a puts debuggerer”(https://tenderlovemaking.com/2016/02/05/i-am-a-puts-debuggerer.html),當我還在玩Ruby的時候,我很喜歡這篇文章。這篇文章的重點是想告訴大家:在許多情況下,你不需要一個成熟的調試器。別誤會我(或者Tenderlove)的意思,我認為,一個好的集成開發環境(IDE)附帶的調試器是開發人員能夠擁有的最強大的工具之一!它能讓你可以很輕鬆地在代碼中放置斷點、在stack trace中移動、或者動態地檢查和修改變量。它也使開發人員在大型代碼庫上工作更加輕鬆,並且可以幫助新手程序員加快新項目的進度。

然而,今天的人們仍然會使用print語句來調試他們的代碼。我總是這樣做,因為打印出一個變量既快又容易。“我要開始調試會話”這句話聽起來很沉重,而“我認為這個變量有問題。我想打印出來看看!”就輕鬆多了。下面就是我們用5分鐘就能寫出的一個print語句的例子:

<code>print(a_varible)

...

if foo:
print(">>>>>>>>>>>>>>Inside 3rd IF")

...

print(">>>>>>>>>>>>>>Inside 37th IF")


print(">>>>>>>>>> #@!?#!!!")/<code>

上面的代碼看起來很熟悉吧?使用print語句進行代碼調試是毫無問題的。很多時候,你要做的只是找出Bug。有時候,這也是你能運用的調試代碼的唯一方法。因為在不影響用戶使用的前提下,你很難將調試代碼添加到生產環境代碼中。然而,僅僅添加一些print語句,然後查看日誌應該不會造成什麼問題。

並不是每個人都在使用一個帶有很棒代碼調試器的集成發環境(IDE)。2019年Stack Overflow開發者調查報告顯示:30.5%的開發人員使用Notepad++,25.4%的開發人員使用Vim,23.4%的開發者使用Sublime Text。這些都是文本編輯器!儘管我看到開發人員在使用Vim時比大多數使用PyCharm或VS Code的用戶更有效率,但是要記住一個文本編輯器並不會帶有強大的代碼調試器。要調試Python代碼,你當然可以使用標準的Python調試器pdb,但是現在你有一個更好的選擇:那就是使用IPython作為調試器。

我使用VS Code已經快兩年了,但我不記得上次使用其內置的調試器是什麼時候。我大部分的調試工作都是在IPython中完成的。以下是我的調試方法:

揭秘 IPython 的 5 种最佳调试方法

將IPython會話嵌入到代碼中

對我而言,最常用的做法是在代碼中嵌入一個IPython會話。你只需要在代碼中插入以下兩行就可以做到:

<code>from IPython import embed
embed/<code>

我喜歡把這兩行代碼放在同一行,像下面這樣:

<code>from IPython import embed; embed/<code>

這樣的話,我只需要敲一次鍵盤就能把它移除掉。而且,在Python中,將多個語句放在同一行是一種壞習慣,因此所有的code linter都會將這行作為一個問題標註出來,這樣的話,當調試完成後,調試就不會忘記把它移除。

當你運行代碼並且解釋器到達帶有embed函數的行時,它將打開一個IPython會話。你可以仔細查看代碼中發生了什麼。當你完成後,你只需要使用Ctrl+d關閉會話,代碼將繼續執行。這種方法的一個優點是,當你關閉IPython會話時,你在IPython中所做的所有修改都將保存下來。因此,你可以用這種方式對變量或函數進行修改(甚至可以用一些簡單的日誌來修飾函數),並查看其餘代碼的行為。

下面是使用embed的簡短演示。假設我們有以下代碼:

<code>a = 10
b = 15

from IPython import embed; embed

print(f"a+b = {a+b}")/<code>

下面是我們運行它時發生的情況:

揭秘 IPython 的 5 种最佳调试方法

如上所見,我更改了a變量的值,當我關閉IPython會話後,a的新值保留下來了。

揭秘 IPython 的 5 种最佳调试方法

在代碼中放置斷點

如果你想查看在給定代碼行上發生的情況,在代碼中嵌入一個IPython會話是不錯的做法。但是它不能像真正的調試器那樣,讓你可以執行下一行代碼。所以更好的辦法是在代碼中放置一個斷點。從Python 版本3.7開始,有一個新的名為breakpoint的內置函數可以用來放置斷點。如果你使用的是較低版本的Python,你可以通過運行以下代碼來實現同樣的效果:

<code>import pdb; pdb.set_trace/<code>

Python的默認調試器(pdb)相當初級。就像在標準Python REPL(—種交互式解釋器環境。R-read)、E-evaluate)、P-print)、L-loop)中一樣,它沒有語法突出顯示或自動縮進的功能。一個更好的選擇是ipdb。它將使用IPython作為調試器。要啟用它,需要將上面代碼中的pdb替換成ipdb:

<code>import ipdb; ipdb.set_trace/<code>

還有另一個有趣的調試器PDB++。它有一組不同於ipdb的特性,例如,一個不斷顯示代碼當前位置的粘滯模式。

不管你最終使用哪個調試器,它們都有一組相當標準的命令。你可以通過調用next命令(或者只是n命令)來執行下一行,通過調用step(或s)命令進入函數內部,使用continue(或c)命令繼續執行直至下一個斷點,使用l或ll命令顯示當前代碼執行的位置,等等。如果你是一個CLI(命令行接口)類型調試器的新手,那麼“使用Pdb調試Python代碼教程”應該對你掌握它們會有幫助。

揭秘 IPython 的 5 种最佳调试方法

%run -d filename.py

IPython還有另外一種方式啟動一個調試器。你不需要像前面那樣修改任何文件的源代碼。你只需要運行%run -d filename.py這個神奇的命令,然後IPython將執行filename.py文件並在其第一行設置一個斷點。作用等同於將import ipdb; ipdb.set_trace手動放入filename.py文件中,並且使用python filename.py命令來運行它一樣。

如果要將斷點放在第一行之外的其他位置,可以使用-b參數。以下代碼將斷點放置在第42行的位置:

<code>%run -d -b42 filename.py/<code>

記住,這裡指定的行必須包含實際執行某些操作的代碼。不能是空行或註釋行!

最後,可能存在這樣一種情形:你希望將斷點放在一個不是你將要運行的文件中。比如說,某個bug可能隱藏在一個導入的模塊中,而你不想再鍵入100次next命令就可以執行到那裡。-b參數可以接受一個文件名,後跟一個冒號和一個行號,以指定要將斷點確切放置到的位置,修改後的代碼如下:

<code>%run -d -b myotherfile.py:42 myscript.py/<code>

上面的代碼將在名為myotherfile.py的文件的第42行設置一個斷點,然後開始執行myotherfile.py文件。一旦Python解釋器到達myotherfile.py,它將在斷點處停止。

揭秘 IPython 的 5 种最佳调试方法

事後調試(Post-mortem debugging)

IPython共有176項特性。事後調試是最好的特性,至少對我來說是這樣。假設你正在運行一個長期運行的腳本,在運行了15分鐘後,它突然崩潰了。那麼,你能做的是設置一些斷點,重新運行它,然後再等待15分鐘,看看發生了什麼嗎?如果你用的是IPython,只要調用一個神奇的命令%debug,你就不必等待了。%debug將加載最後一個異常發生時的stack trace,並且啟動調試器(Python將最後一個未處理的異常存儲在sys.last_traceback變量中)。這是一個很好的特性,它可以讓你直接啟動調試器,節省了幾個小時的重新運行一些命令的時間。

如果使用的是標準pdb調試器,則可以通過運行import pdb; pdb.pm命令來實現相同的效果。

揭秘 IPython 的 5 种最佳调试方法

使用%pdb自動啟動調試器

讓調試更加方便的唯一方法是在發生異常時自動啟動調試器。IPython中的這個神奇的命令 - %pdb就能實現這一點。

如果運行%pdb 1(或%pdb on)命令,調試器將在每個未處理的異常上自動啟動。你可以使用%pdb 0或關閉%pdb on命令關閉這個功能。不帶任何參數運行地運行%pdb將在打開和關閉自動調試器之間進行切換。

原文:https://switowski.com/blog/ipython-debugging

本文為 CSDN 翻譯,轉載請註明來源出處。


分享到:


相關文章: