Python

Python 使用 assert 在開發階段協助除錯教學與範例

本篇介紹如何在 Python 中使用 assert 敘述,協助程式設計者在開發階段偵測程式臭蟲(bugs)。

Python 的 assert 敘述是一個很實用的除錯輔助工具,可在程式開發階段協助程式設計者偵測不該發生的條件是否發生,藉此提升程式的穩定與可靠性。

assert 敘述使用方式

assert 的語法如下:

assert 判斷條件[, 錯誤訊息]

如果 判斷條件False 的時候,就會引發 AssertionError 例外,而 錯誤訊息 則是可有可無的參數,可用來指定要給使用者看的訊息,若 AssertionError 例外被觸發,則該訊息就會附加在例外的錯誤訊息中。

以下是一個簡單的 assert 使用範例,假設我們定義了一個函數 computePassRatio,傳入測驗通過(passCount)與沒通過(failCount)的人數,然後計算出本次測驗的通過率,理論上每一的測驗的總人數一定都會大於零(若完全沒人參與,就根本沒辦法進行測驗),但是為了避免程式設計者不小心寫錯程式,所以我們在這裡使用 assert 額外對總人數進行檢查,確保總人數是大於零的:

# 計算測驗通過率
def computePassRatio(passCount, failCount):

    # 計算總人數
    sumCount = passCount + failCount

    # 檢查總人數
    assert sumCount > 0, "總人數必須大於零"

    # 計算通過率
    return passCount / sumCount

如果這時候程式不小心寫錯,出現了完全沒有人進行測驗,卻要計算通過率的狀況,就會觸發例外:

# 錯誤的呼叫
computePassRatio(0, 0)
Traceback (most recent call last):
  File "assert_demo.py", line 13, in 
    computePassRatio(0, 0)
  File "assert_demo.py", line 8, in computePassRatio
    assert sumCount > 0, "總人數必須大於零"
AssertionError: 總人數必須大於零

避免誤用括號

assert 敘述的語法跟函數呼叫不同,不能加上括號,如果不小心加上括號,就會變成傳入一個 tuple 作為判斷式,這樣就會造成永遠都是 True 的判斷式,Python 3 直譯器若遇到永遠都是成立的 assert 敘述也會發出警告:

# 錯誤示範:誤用括號
assert(1 == 2, "錯誤訊息")
assert_brackets.py:1: SyntaxWarning: assertion is always true, perhaps remove parentheses?
  assert(1 == 2, "錯誤訊息")

程式最佳化

assert 語法在實際執行時,會被 Python 直譯器轉換為類似這樣的程式碼:

if __debug__:
    if not 判斷條件:
        raise AssertionError(錯誤訊息)

這裡的全域變數 __debug__ 是一個 Python 內建的旗標變數,普通情況下它的值會是 True,但是當 Python 直譯器啟用了最佳化編譯選項時,__debug__ 就會被設定為 False

而 Python 程式在檢查 assert 的判斷條件之前,會先檢查 __debug__ 這個全域變數是否為 True,若 __debug__True 才會繼續檢查指定的判斷條件,也就是說在程式啟用了最佳化編譯選項後,所有的 assert 檢查都會被忽略。

我們可以在執行 Python 程式時加上 -O-OO 參數來啟用最佳化編譯:

# 啟用基本最佳化編譯
python -O assert_demo.py

# 啟用進階最佳化編譯
python -OO assert_demo.py

另一種啟用最佳化編譯的方式是設定 PYTHONOPTIMIZE 環境變數:

# 啟用基本最佳化編譯
export PYTHONOPTIMIZE=1
python assert_demo.py

# 啟用進階最佳化編譯
export PYTHONOPTIMIZE=2
python assert_demo.py

assert 適用情況

由於 assert 敘述只會在 __debug__ 旗標變數設定為 True 的時候有作用,當程式布署至正式環境中時,所有 assert 敘述有可能就會被完全忽略,所以 assert 只適合用於開發與測試階段的各種確保性條件檢查(省略亦可正常運作的檢查),檢查程式本身有沒有寫錯(bugs),但不可以用來檢查輸入資料的正確性或是各種程式本身的邏輯判斷。

另外 assert 在進行條件判斷時也不可以產生任何邊際效應(更改了程式中的某些資料),因為如果在 assert 敘述中會對資料進行某些操作,而在正式執行階段又省略了所有 assert 敘述,就會產生很難釐清的程式臭蟲。以下是一個簡單的錯誤示範:

myList = [1, 2, 3]

# 錯誤寫法:含有邊際效應的 assert 敘述
assert myList.pop() == 3

print(myList)

將這段指令搞儲存為 side_eff.py 之後,以不同的方式執行,卻會得到不同的結果:

# 正常執行
python side_eff.py
[1, 2]
# 啟用最佳化編譯
python -O side_eff.py
[1, 2, 3]

這就是由於 assert 敘述隱含邊際效應所造成的程式臭蟲,像這樣的程式臭蟲只會在正式執行階段出現,在開發與測試階段事不會發生的,在除錯上非常麻煩。

多行 assert 敘述

assert 敘述的內容過長,可以使用行尾反斜線的方式拆成多行撰寫:

# 以行尾反斜線分為多行撰寫
assert 123456789 == 0.123456789, \
        "這是一條非常冗長的錯誤訊息。"

參考資料

Share
Published by
Office Guide

Recent Posts

Python 使用 PyAutoGUI 自動操作滑鼠與鍵盤

本篇介紹如何在 Python ...

9 個月 ago

Ubuntu Linux 以 WireGuard 架設 VPN 伺服器教學與範例

本篇介紹如何在 Ubuntu ...

10 個月 ago

Linux 網路設定 ip 指令用法教學與範例

本篇介紹如何在 Linux 系...

10 個月 ago

Windows 使用 TPM 虛擬智慧卡保護 SSH 金鑰教學與範例

本篇介紹如何在 Windows...

11 個月 ago

Linux 以 Shamir’s Secret Sharing 分割保存金鑰教學與範例

介紹如何在 Linux 中使用...

11 個月 ago

Linux 以 Cryptsetup、LUKS 加密 USB 隨身碟教學與範例

介紹如何在 Linux 系統中...

11 個月 ago