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 ...

1 年 ago

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

本篇介紹如何在 Ubuntu ...

1 年 ago

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

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

1 年 ago

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

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

1 年 ago