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'

以上述例外錯誤訊息為例,當程式出現錯誤的時候,我們通常會有幾個疑問:

  1. TypeError: unsupported operand type(s) for +: 'int' and 'tuple' 是什麼意思?
  2. 「 那出錯當時 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.

對抗久坐職業傷害

研究指出每天增加 2 小時坐著的時間,會增加大腸癌、心臟疾病、肺癌的風險,也造成肩頸、腰背疼痛等常見問題。

然而對抗這些問題,卻只需要工作時定期休息跟伸展身體即可!

你想輕鬆改變現狀嗎?試試看我們的 PomodoRoll 番茄鐘吧! PomodoRoll 番茄鐘會根據你所設定的專注時間,定期建議你 1 項辦公族適用的伸展運動,幫助你打敗久坐所帶來的傷害!

贊助我們的創作

看完這篇文章了嗎? 休息一下,喝杯咖啡吧!

如果你覺得 MyApollo 有讓你獲得實用的資訊,希望能看到更多的技術分享,邀請你贊助我們一杯咖啡,讓我們有更多的動力與精力繼續提供高品質的文章,感謝你的支持!