DeBug Python代碼全靠print函數(shù)?換用這個一天2K+Star的工具吧
print 函數(shù)已老,DeBug 該靠 PySnooper 了~
小伙伴們,你們都怎樣 DeBug Python 代碼?是不是常用 print 大法?在本文介紹的這個項目中,deBug Python 代碼再也不需要 print 了。只要給有疑問的代碼加上裝飾器,各種信息一目了然,找出錯誤也就非常簡單了。
這個名為 PySnooper 的項目是剛開源的,僅僅一天就獲得了 2K+ 的 Star 量,當(dāng)然這「一天」還沒結(jié)束,這個收藏量也會繼續(xù)刷新。
項目地址:https://github.com/cool-RR/pysnooper
Python 怎樣 DeBug?
如果寫著寫著模型,發(fā)現(xiàn)模型不 work 了,那么你該怎樣找出 Python 的錯誤語句?這種錯誤一般與語法無關(guān),而是某個變量的運算發(fā)生了錯誤。接下來我們就要慢慢找哪個地方有問題了,這里最常見、最直觀的方法就是 print 大法。把我們懷疑的變量打印出來,總會找到異常的地方。
如果代碼中嵌入了單元測試,例如 assert 語句,那么還能縮小一些懷疑范圍。但通常我們都要多次嘗試,打印多個變量才能找到錯誤的地方。在 PyTorch 或 Keras 這樣的動態(tài)計算圖還好,打印出來的直接是一個值,像 TensorFlow 這樣的靜態(tài)計算圖,打印出來是張量信息而不是值,這就很尷尬了。
實際上不止是機器學(xué)習(xí),在我們寫 Python 的時候,總是想搞清楚為什么寫的代碼在運行時有點不大對。很多讀者樂于使用斷點等成熟的 DeBug 工具,也有的直接使用 print 大法找錯誤的地方。但現(xiàn)在我們不需要擔(dān)心了,本文將介紹一個新的開源工具,它信心滿滿地呼吁到:「不要再使用 print 函數(shù)來 DeBug 啦~」
極簡DeBug工具PySnooper
一般情況下,想要知道哪一行代碼在運行、哪一行不運行、本地變量的值是多少時,大部分人會使用 print 函數(shù),在關(guān)鍵部分打印某個或某組變量的值、形狀、類型等信息。
而 PySnooper 讓你能快速地獲得這些信息,且相比之下它不需要細致地寫 print 函數(shù),只需要向感興趣的函數(shù)增加一個裝飾器就行了。我們會得到該函數(shù)的詳細 log,包含哪行代碼能運行、什么時候運行以及本地變量變化的確切時間。
相比于其他代碼智能工具,PySnooper 為何如此優(yōu)秀?因為不需要任何設(shè)置,你就可以在劣等、不規(guī)則的企業(yè)代碼庫上使用 PySnooper。只需要加個裝飾器,并為日志輸出地址指定路徑就行了。
這樣說可能不太直觀,下面我們可以具體看個案例,PySnooper 的優(yōu)秀就能一目了然。
PySnooper 案例
下面項目作者寫了一個函數(shù)以將數(shù)值轉(zhuǎn)換為二進制碼,該函數(shù)返回的是一個二進制列表。下面我們將裝飾器 @pysnooper.snoop() 加到該函數(shù)上,就大功告成了。
- import pysnooper
- @pysnooper.snoop()
- def number_to_bits(number):
- if number:
- bits = []
- while number:
- number, remainder = divmod(number, 2)
- bits.insert(0, remainder)
- return bits
- else:
- return [0]
- number_to_bits(6)
該函數(shù)返回的日志如下,我們可以看到在調(diào)用 number_to_bits 函數(shù)時,賦予參數(shù) number 的初始值為 6。然后,PySnooper 就還是對著源代碼一行行分析了。
如上分析所示,函數(shù)每創(chuàng)建一個新變量,那么這個變量的值、這個變量的變化都會展示出來。而且 PySnooper 還將循環(huán)展開,因此變化的細節(jié)更加明確。最終 6 的二進制版本應(yīng)該是 [1, 1, 0],它的變化過程也展示在 bits 變量中。
現(xiàn)在通過這些詳細信息,PySnooper 再也不用擔(dān)心我們用 print 函數(shù)強行 deBug 了。
PySnooper 詳細特征
如果標(biāo)準(zhǔn)錯誤輸出難以獲得,或者太長了,那么可以將輸出定位到本地文件:
- @pysnooper.snoop('/my/log/file.log')
查看一些非本地變量的值:
- @pysnooper.snoop(variables=('foo.bar', 'self.whatever'))
展示我們函數(shù)中調(diào)用函數(shù)的 snoop 行:
- @pysnooper.snoop(depth=2)
將所有 snoop 行以某個前綴開始,更容易定位和找到:
- @pysnooper.snoop(prefix='ZZZ ')
演示 PySnooper
下面我們最開始嘗試使用 PySnooper 獲取 TensorFlow 的信息,如果它能獲取各種張量信息,那可就太強大了。
首先使用 pip 安裝包:
- pip install pysnooper
果然,TensorFlow 這種靜態(tài)圖并不能很好地獲取信息,讀者也可嘗試一下。后面我們試了試 NumPy,希望能獲取整個計算流的信息。如下代碼所示,我們創(chuàng)建了兩個數(shù)組變量,并且 2×2 的矩陣會連乘多次,如果能追蹤到這種連乘,那就比較好處理錯誤。
- import pysnooper
- import numpy as np
- @pysnooper.snoop()
- def multi_matmul(times):
- x = np.random.rand(2, 2)
- w = np.random.rand(2, 2)
- for i in range(times):
- x = np.matmul(x, w)
- return x
- multi_matmul(3)
對于 NumPy,該工具確實能追蹤所有可疑變量的變化過程。當(dāng)然在實際運算中,矩陣乘法的維度會非常大,我們可以直接追蹤形狀(Shape),而不是具體的值。
【本文是51CTO專欄機構(gòu)“機器之心”的原創(chuàng)譯文,微信公眾號“機器之心( id: almosthuman2014)”】