Python 好用套件介紹 - better-exceptions
Posted on Aug 25, 2023 in Python 模組/套件推薦 , Python 程式設計 - 初階 by Amo Chen ‐ 3 min read
相信大家學寫程式都有相同的一段經驗,那就是看不懂例外錯誤(exceptions)訊息的意思,你可能每個單字都認識,但組合起來就像天書一樣難以理解⋯⋯。
如果有更容易理解的例外錯誤訊息的話,相信會減輕大家在學習與除錯的痛點!
本文要介紹的 better-exceptions 套件,是一個不管新手、老手都適用的套件,它改良了 Python 的例外錯誤訊息,把錯誤當下的變數值一併顯示在例外錯誤訊息當中,就這一個貼心的舉動,大大改善大家在學習與除錯的體驗。
是一個值得推薦與擁有的套件!
本文環境
- Python 3
- better_exceptions
$ pip install better_exceptions
better-exceptions 介紹
以下是一段一定會出現錯誤的程式(不懂沒關係,並非本文重點):
import math
from collections import Counter
def shannon_entropy(s):
counter = Counter()
for c in s:
counter[c] +=1
total = sum(counter.items())
entropy = 0.0
for k, v in counter.items():
entropy -= (v / total) * math.log2(v / total)
return entropy
def main():
print(shannon_entropy('Hello, World'))
if __name__ == '__main__':
main()
執行之後,就會出現一個典型的 Python 例外錯誤訊息,如下所示:
Traceback (most recent call last):
File "test_be.py", line 23, in <module>
main()
File "test_be.py", line 19, in main
print(shannon_entropy('Hello, World'))
File "test_be.py", line 10, in shannon_entropy
total = sum(counter.items())
TypeError: unsupported operand type(s) for +: 'int' and 'tuple'
以上述例外錯誤訊息為例,當程式出現錯誤的時候,我們通常會有幾個疑問:
- 「
TypeError: unsupported operand type(s) for +: 'int' and 'tuple'
是什麼意思? - 「 那出錯當時
counter
的值到底是什麼?」
這時不外乎是修改程式,將值給 print 或 log 下來,再一步步追到問題根本。
但如果有了 better-exceptions, 你可能直接就理解問題核心了。
以下是安裝 better-exceptions 後,相同程式執行結果,可以看到 better-exceptions 直接幫我們把出錯當下的值顯示出來,可以明顯看到 counter
當時的值是一個 key 為字串,值為整數的 Counter , 而 items()
方法會回傳一個 iterator, 這個 iterator 會不斷 yield (key, value)
的 tuple 出來,這就導致 sum
函式無法與 tuple 進行加總運算,導致錯誤發生。
Traceback (most recent call last):
File "test_be.py", line 23, in <module>
main()
└ <function main at 0x1011e9dc0>
File "test_be.py", line 19, in main
print(shannon_entropy('Hello, World'))
└ <function shannon_entropy at 0x100f16160>
File "test_be.py", line 10, in shannon_entropy
total = sum(counter.items())
└ Counter({'l': 3, 'o': 2, 'H': 1, 'e': 1, ',': 1, ' ': 1, 'W': 1, 'r': 1, 'd': 1})
TypeError: unsupported operand type(s) for +: 'int' and 'tuple'
所以我們只要將 counter.items()
改為 counter.values()
就解決問題了。
這就是使用 better-exceptions 所帶來的好處,省時省力又省心!
啟用 better-exceptions
透過 pip 指令安裝之後, better-exceptions 預設是不會啟用的, better-exceptions 的啟用需要設定 BETTER_EXCEPTIONS
環境變數,值可以是任意值(包含 0 也代表啟用)。
Linux 或 macOS:
export BETTER_EXCEPTIONS=1 # Linux / macOS
Windows:
setx BETTER_EXCEPTIONS 1 # Windows
設定好之後, Python 如果出現例外錯誤訊息就會自動使用 better-exceptions ,完全不需要我們 import 。
如果想關掉的話,只要刪除 BETTER_EXCEPTIONS
環境變數即可:
Linux 或 macOS:
unset BETTER_EXCEPTIONS # Linux / macOS
Windows:
reg delete "HKCU\Environment" /v BETTER_EXCEPTIONS /f # Windows
better-exceptions 與 unittest 模組 / Django 整合
better-exceptions 也提供可以跟 unittest 模組整合的方法,如下:
import sys
import unittest
import better_exceptions
def patch(self, err, test):
lines = better_exceptions.format_exception(*err)
if sys.version_info[0] == 2:
return u"".join(lines).encode("utf-8")
return "".join(lines)
unittest.result.TestResult._exc_info_to_string = patch
其實是把 TestResult 類別處理例外的函式換成 better_exceptions 的,不過這個方法不是正規的使用方式,無法保證未來的 Python 版本也還會有 _exc_info_to_string
屬性存在。
Django 的部分則是提供 better_exceptions.integrations.django.BetterExceptionsMiddleware
Middleware 可以添加到 Django 的 settings.py
之中,詳見 官方文件 。
總結
一般來說開發/測試時,相對容易重現錯誤,也許不使用 better-exceptions 也沒有關係,但如果是像 production 環境,除了無法介入之外,你也無從請使用者重新操作一遍,因此在發生錯誤的當下,你無法得知當時執行時的值為何,這時如果要除錯就需要大膽假設、細心求證,不見得能很快解決問題,但有了 better-exceptions 的話,你等於多了 1 個線索,就能夠更輕鬆更快地進行除錯、修正!
這麼好的套件,值得擁有!
Happy Coding!
References
Qix-/better-exceptions: Pretty and useful exceptions in Python, automatically.