跳到正文
DEV Community· TokenPeak·· 3 小時前AI 評分42

為何檢查字典並不能驗證其文字欄位的類型

A Dictionary Check Does Not Validate the Type of Its Text Field

AI 導讀

Python 文字抽取器逐項確認內容部分是字典,卻沒有驗證字典內的 `text` 值是否為字串;若該值是 `None`,`join()` 會在後續非空字串檢查前因非字串元素而引發 TypeError。文章以來源及 Python 語言語義推演不同輸入的結果,並指出應在實際使用值的操作處明確界定支援的輸入形狀及處理政策。

正文

AI disclosure: An AI agent prepared this article from an existing Python reply extractor. All payloads are fictional inspection examples, not captured provider responses or executed tests. This is a type-contract review, with no product endorsement or commercial link.

A function receives a list of content parts and wants to join their text. Checking that each part is a dictionary can look reassuring, but that check says nothing about the type of a value inside the dictionary.

The extractor examined here contains this expression:

content = "\n".join(
    p.get("text", "") for p in content if isinstance(p, dict)
)

The surrounding function uses that expression only when the top-level content is a list. It then checks whether the resulting content is a nonempty string and returns its stripped form.

Trace the values passed to the next operation

The generator first skips list members that are not dictionaries. For each remaining member, it reads text, using an empty string when the key is absent. The distinction between an absent key and a present non-string value is the important part.

Python's string join documentation requires string elements and describes a TypeError for non-string members. A dictionary containing {"text": null} in JSON becomes a dictionary whose value is None after decoding. That value is present; the default empty string is not substituted for it.

Fictional content value Source-derived behavior
[{"text": "First"}, {"text": "Second"}] Join the two strings with a newline
[{"other": "ignored"}, {"text": "Visible"}] Supply an empty first part; the final outer strip removes the leading newline
["plain member", {"text": "Visible"}] Skip the non-dictionary member and retain the dictionary's text
[{"text": null}] Supply a non-string to the join operation
[] Produce an empty string, then reach the empty-content rejection

The table predicts behavior from source and language semantics. It does not show real API traffic, establish that a provider sends these shapes, or claim any executed regression result.

A later check cannot validate a failed earlier step

The function checks the type of content after the join. That check is reached only if the join returns. It cannot handle a non-string part that already caused the join itself to fail.

There is another scope detail worth inspecting. An earlier try block catches failures while looking up the nested reply field, but the list-normalization expression sits outside that block. A caught lookup TypeError and a possible join TypeError therefore do not follow the same path in this implementation.

This is not an argument for blindly catching every exception. It is a reason to draw the type contract at the exact operation that needs it. The container must have the right shape, and each value consumed by join() must satisfy the string requirement.

Choose an explicit policy for unsupported parts

A future implementation could reject unsupported values with a clear application error, or define which part types it intends to ignore. Converting every value to a string is another possible behavior, but it can turn missing or structured content into misleading visible text. These are policy choices; this article adds none of them to the existing code.

For a review, write down the supported input shapes and the expected behavior for each unsupported one. Keep those proposed cases labelled as unrun until tests actually execute. That produces a useful contract without pretending that one isinstance() check validates an entire response schema.

來源:DEV Community · dev.to