iT邦幫忙

2026 iThome 鐵人賽

DAY 13
0
佛心分享-IT 人自學之術

It Works on My Machine:30 天從踩雷學會工程事故調查系列 第 13

Day 13|重現:一個 Tuple 如何一路通過沒有意見的函式

  • 分享至 

  • xImage
  •  

本篇是故事三的「重現」篇。

本篇要回答:錯誤型別為什麼能傳這麼遠?第一個把它擋下來的邊界在哪裡?

當時發生了什麼

Day 11 說錯誤在「很下游」才現形。這一篇回答為什麼:因為中間每一層都對 tuple 沒有意見。

事件軌跡:

變數賦值(型別在這裡變形)
  ↓
函式參數(只要求「可迭代」,tuple 完全合格)
  ↓
資料轉換(len、sum、迴圈——全部照常運作)
  ↓
序列化、ORM 或 API 邊界(結構第一次被寫死)
  ↓
下游才發現型別或結構不符

我原本怎麼判斷

我原以為錯的資料會很快撞牆。實際上 Python 的慣例是鴨子型別:函式普遍只要求「行為像」,不要求「就是」。這是語言的優點——同一個特性,讓正確的多型與錯誤的湊合都暢通無阻。

我怎麼查證或重現

最小重現,每一步都實際執行過:

value = 42
result = value,          # (42,)

len(result)              # 1 —— 合法,只是意義已經不對
[x for x in result]      # [42] —— 可迭代,照常通過

def total(items):
    return sum(len(str(i)) for i in items)
total(("abc",))          # 3 —— 接受任何 iterable 的函式毫無意見
total("abc")             # 3 —— 順帶一提:字串也是 iterable,另一個經典陷阱

import json
json.dumps((42,))        # '[42]' —— 邊界第一次改變資料形狀

注意最後一行:json.dumps 不報錯,它把 tuple 靜靜序列化成 JSON 陣列。原本下游期待的 42 變成了 [42]——第一個「把結構寫死」的邊界不是擋下錯誤,而是把錯誤翻譯成另一種格式送出去。真正的例外要等到更下游有人對 [42] 做數值運算或 schema 驗證時才引爆,而那裡的 Stack Trace 不會指向逗號。

三個核心問題的答案於是清楚了:哪些函式沒有立即失敗?所有只要求 iterable 的。哪個邊界第一次要求精確結構?序列化與資料契約邊界。錯誤發生點與根因相距多遠?隔著整條呼叫鏈加一次格式轉換。

區分證據等級。已確認事實:上述每一行的行為(已執行驗證)。合理推論:當時的事故沿同型路徑滑行。

今天留下什麼方法

本篇結論:

真正需要重現的不是最後一個例外,而是錯誤型別如何一路被當成正常資料傳下去。

下一篇(Day 14)把這條路徑翻譯成證據的語言:語法正確、程式可跑、資料契約正確,是三種不同的證據。


上一篇
Day 12|查證:不要只印出值,還要確認它到底是什麼
下一篇
Day 14|理解:語法正確、程式可跑、資料契約正確,是三種不同證據
系列文
It Works on My Machine:30 天從踩雷學會工程事故調查14
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言