翻譯:《實用的Python程式設計》08_03_Debugging

codists發表於2021-04-11

目錄 | 上一節 (8.2 日誌) | 下一節 (9 包)

8.3 除錯

除錯建議

假設程式崩潰了:

bash % python3 blah.py
Traceback (most recent call last):
  File "blah.py", line 13, in ?
    foo()
  File "blah.py", line 10, in foo
    bar()
  File "blah.py", line 7, in bar
    spam()
  File "blah.py", 4, in spam
    line x.append(3)
AttributeError: 'int' object has no attribute 'append'

那麼現在該怎麼辦呢?

閱讀回溯資訊

最後一行是程式崩潰的具體原因:

bash % python3 blah.py
Traceback (most recent call last):
  File "blah.py", line 13, in ?
    foo()
  File "blah.py", line 10, in foo
    bar()
  File "blah.py", line 7, in bar
    spam()
  File "blah.py", 4, in spam
    line x.append(3)
# Cause of the crash
AttributeError: 'int' object has no attribute 'append'

不過,回溯資訊並不總是那麼易於閱讀或理解。

專業建議:將整個回溯貼上到谷歌。

使用互動式直譯器(REPL)

執行指令碼的 時候,可以使用選項 -i 使 Python 保持存活(keep alive)。

bash % python3 -i blah.py
Traceback (most recent call last):
  File "blah.py", line 13, in ?
    foo()
  File "blah.py", line 10, in foo
    bar()
  File "blah.py", line 7, in bar
    spam()
  File "blah.py", 4, in spam
    line x.append(3)
AttributeError: 'int' object has no attribute 'append'
>>>

選項 -i 可以保留直譯器狀態。這意味著可以在程式崩潰後查詢錯誤資訊。對變數的值和其它狀態進行檢查。

使用列印進行除錯

使用 print() 函式進行除錯非常常見。

建議:確保使用的是 repr() 函式

def spam(x):
    print('DEBUG:', repr(x))
    ...

repr() 函式顯示一個值的準確表示,而不是格式良好的輸出。

>>> from decimal import Decimal
>>> x = Decimal('3.4')
# NO `repr`
>>> print(x)
3.4
# WITH `repr`
>>> print(repr(x))
Decimal('3.4')
>>>

Python 的偵錯程式

可以在程式內手動啟動偵錯程式(debugger)。

def some_function():
    ...
    breakpoint()      # Enter the debugger (Python 3.7+)
    ...

上述操作會在 breakpoint() 呼叫時啟動偵錯程式。

在 Python 的早期版本中,可能會看到下面這樣的除錯指南:

import pdb
...
pdb.set_trace()       # Instead of `breakpoint()`
...

(譯註:Python 3.7 之後,可以使用內建函式 breakpoint() 代替 import pdb; pdb.set_trace()

在除錯直譯器下執行程式

也可以在偵錯程式下執行整個程式:

bash % python3 -m pdb someprogram.py

上述操作會在第一行語句之前自動進入偵錯程式,允許設定斷點和修改配置。

常見的偵錯程式命令:

(Pdb) help            # Get help
(Pdb) w(here)         # Print stack trace
(Pdb) d(own)          # Move down one stack level
(Pdb) u(p)            # Move up one stack level
(Pdb) b(reak) loc     # Set a breakpoint
(Pdb) s(tep)          # Execute one instruction
(Pdb) c(ontinue)      # Continue execution
(Pdb) l(ist)          # List source code
(Pdb) a(rgs)          # Print args of current function
(Pdb) !statement      # Execute statement

斷點的位置可以用下列任意一種方式進行表示:

(Pdb) b 45            # Line 45 in current file
(Pdb) b file.py:45    # Line 34 in file.py
(Pdb) b foo           # Function foo() in current file
(Pdb) b module.foo    # Function foo() in a module

練習

練習 8.4:Bugs? 什麼是 Bugs?

有 bug,我們就解決 bug(It runs. Ship it!)。

目錄 | 上一節 (8.2 日誌) | 下一節 (9 包)

注:完整翻譯見 https://github.com/codists/practical-python-zh

相關文章