Python Note 04

Python的Debug神器

在日常除錯過程中,我們經常會使用 print()函數查看變數內容,以確認程式執行的結果是否如預期。然而,這樣做往往需要花費大量時間去逐一檢查輸出的正確性。在 Python 中,有一個內建函數叫做 breakpoint(),專門用於啟動偵錯器。當執行到 breakpoint() 時,程序會暫停,並進入一個互動式的調試環境。透過這個功能,你可以查看變數的值、逐步執行程式碼,以及有效地進行問題調試。

breakpoint()的主要功能:

  1. 暫停程序執行:當程式執行到breakpoint()時,程式會暫停,進入偵錯器。
  2. 進入互動式調試環境:你可以查看目前的變數、執行下一步操作、檢查堆疊幀等。
  3. 調試程式碼:你可以使用偵錯器的命令(例如n進入下一步、c繼續運行、q退出調試器等)來逐步執行程式碼,幫助你發現問題。

breakpoint()在Python 3.7 版本中被引入,它會調用sys.breakpointhook(),通常會啟動預設的Python 偵錯器pdb

常見調試器命令:

  • n (next):執行下一行程式碼。
  • s (step):執行下一行程式碼,如果該行為函數呼叫則進入函數,停在函數第一行。
  • w (where):列出從當前執行點到最外層調用的一系列函數調用和行號。
  • c (continue):繼續執行直到遇到下一個斷點或程式結束。
  • q (quit):退出偵錯器並結束程式。
  • p (print):列印變數的值,例如p some_variable
  • l (list):印出目前所在 function/frame上下 10 行的程式碼。
  • ll (long list):印出目前所在 function/frame 的所有程式碼。
  • r (return):繼續執行到當前函數返回為止。

使用breakpoint()的範例:

運行範例:

def calculate_area(length, width):
    area = length * width
    breakpoint()
    return area

length = 5
width = 10
area = calculate_area(length, width)
print(f"The area is {area}")

當程式碼執行到breakpoint()時,會進入偵錯模式,你可以使用偵錯器指令來查看變數的值或逐步執行程式碼。

輸出(在進入調試模式後):

> <filename>(5)calculate_area()
-> return area
(Pdb) p length
5
(Pdb) p width
10
(Pdb) p area
50
(Pdb) n
(Pdb) w
/media/lukaslu/2E7C3ACF7C3A9217/user_data/Jim/code/MongoDB_MinIO_FastAPI/test.py(8)<module>()
-> area = calculate_area(length, width)
> /media/lukaslu/2E7C3ACF7C3A9217/user_data/Jim/code/MongoDB_MinIO_FastAPI/test.py(4)calculate_area()
-> return area

在這個範例中,程式會在breakpoint()處暫停,你可以使用p指令查看變數lengthwidtharea的值;使用n逐步執行程式碼;使用w可以查看從程式入口到當前中斷點的完整調用棧,箭頭 -> 指向當前正在執行的行。

小技巧

使用 breakpoint() 的一些小技巧可以讓你的 Python 調試過程更加高效和便利。以下是一些實用的建議:

條件斷點

你可以在 breakpoint() 函數中添加條件,只有當條件為真時,才會觸發斷點。這在處理循環或條件語句中特別有用。

for i in range(100):
    if i == 50:
        breakpoint()  # 只在 i 等於 50 時暫停

動態設置和移除斷點

通過編程方式設置和移除斷點,可以根據運行時數據或條件動態地改變調試策略。

if some_condition:
    breakpoint()  # 動態設置斷點

整合日誌

使用 breakpoint() 時,考慮在進入調試器前後添加日誌語句,這樣可以在不進入調試器的情況下追蹤程式的運行狀態。

logging.debug('About to hit breakpoint')
breakpoint()
logging.debug('Resuming from breakpoint')

自定義 sys.breakpointhook

你可以自定義 breakpoint() 的行為,通過設置 sys.breakpointhook。這允許你在觸發斷點時執行自定義代碼或調用其他調試器,如 ipdb,提供了 pdb 的所有功能,同時增加了語法高亮、Tab 補全和更詳細的調用棧資訊,使得調試過程更直觀和用戶友好。提

import sys

def my_debugger():
    import ipdb
    print("Custom debugging session starts.")
    ipdb.set_trace()

sys.breakpointhook = my_debugger

# Somewhere in your code
breakpoint()  # This will trigger `my_debugger` instead of the default pdb.

進行性能分析

在性能關鍵部分的代碼中使用breakpoint(),檢查特定操作前後的系統狀態或變數,使用如 memory_profilertime 模塊來檢測記憶體使用或時間消耗,有助於識別效能瓶頸。

from memory_profiler import memory_usage
import time
import pdb

def perform_heavy_operations():
    # 假設這是一個記憶體和處理時間密集型操作
    sum = 0
    for i in range(10000000):
        sum += i
    return sum

# 檢測記憶體和時間使用
initial_memory = memory_usage()
start_time = time.time()

# 執行密集型操作
result = perform_heavy_operations()

# 斷點設置
breakpoint()  # 在此處觸發斷點以檢視當前狀態

final_memory = memory_usage()
end_time = time.time()

# 計算使用的記憶體和時間
memory_used = final_memory[0] - initial_memory[0]  # 單位為 MiB
time_taken = end_time - start_time  # 單位為秒

print(f"Result of operations: {result}")
print(f"Memory used: {memory_used} MiB")
print(f"Time taken: {time_taken} seconds")

總結

breakpoint() 的基本功能:

  • 暫停程序執行:在指定的代碼點暫停執行,進入偵錯模式。
  • 互動式調試環境:允許查看變數、逐步執行程式碼、檢查調用棧等。
  • 簡化調試過程:提供一個直觀的方式來逐步通過程式碼,找出錯誤的來源。

breakpoint() 使用技巧:

  • 條件斷點:在符合特定條件時才觸發斷點,適用於循環或條件語句中。
  • 動態斷點:根據運行時數據或條件動態設置斷點,以靈活調整調試策略。
  • 整合日誌:在進入和離開調試器時記錄日誌,方便追蹤程式運行狀態。
  • 自定義 sys.breakpointhook:自定義斷點行為,可整合其他工具或執行特定調試代碼。

使用 breakpoint() 進行性能分析:

  • 檢測記憶體和時間使用:使用 memory_profilertime 模塊在斷點前後測量記憶體和執行時間,以識別效能瓶頸。
  • 分析操作效果:透過斷點檢視特定操作對系統資源的影響,並評估優化策略。

這些總結點幫助你更有效地利用 breakpoint() 來進行錯誤排查和性能分析,提升開發效率和程式的運行效能。