## 1. 一句話總結

## 2. TypedDict:長得像 dict,但欄位被鎖定
`TypedDict` 讓你在**用字典的同時**,
告訴 type checker
「這個 dict 應該有哪些 key、
每個 key 是什麼型別」。
from typing import TypedDict
class Point(TypedDict):
x: int
y: int
# 建立時就是一個普通 dict
p: Point = {"x": 1, "y": 2}
print(p["x"]) # 讀取用中括號 → 1
p["x"] = 10 # ✅ 可以修改
print(p) # {'x': 10, 'y': 2}
### 另一種寫法:函式式建立法
跟 `NamedTuple` 一樣,
`TypedDict` 也有「函式呼叫式」寫法,兩者長得很像:
from typing import NamedTuple, TypedDict
PointNT = NamedTuple("PointNT", [("x", int), ("y", int)]) # list of (名稱, 型別)
PointTD = TypedDict("PointTD", {"x": int, "y": int}) # dict {名稱: 型別}
p: PointTD = {"x": 1, "y": 2} # 用法和 class 版完全一樣差別只在第二個參數的容器:
`NamedTuple` 用 **list of tuple**,
`TypedDict` 用 **`{欄位: 型別}` dict**。
**什麼時候才需要函式式?**
平常優先用 class 版(最好讀、IDE 支援最完整)。
只有欄位名**不是合法 Python 識別字**
(含連字號、空格、數字開頭)時,
class 版寫不出來,才非用函式式:
# "release-year" 有連字號,不是合法變數名稱,class 版無解,只能函式式
Movie = TypedDict("Movie", {"name": str, "release-year": int})### 好處:打錯 key 會被抓到
p: Point = {"x": 1, "y": 2}
p["z"] = 3 # ⚠️ type checker 會警告:Point 沒有 z 這個 key
value = p["xx"] # ⚠️ 拼錯 key,type checker 會提醒> 注意:`TypedDict` 的檢查是**靜態**的
(由 Pylance / mypy 等工具在寫程式時提醒),
> 執行期它就是一個普通 dict,Python 本身不會真的擋下你。
### 天生就能轉 JSON
因為它本體是 dict,`json.dumps` 會**保留鍵名**:
import json
p: Point = {"x": 1, "y": 2}
print(json.dumps(p)) # {"x": 1, "y": 2} ← 欄位名還在## 3. NamedTuple:長得像 class,但本質是 tuple
`NamedTuple` 讓你用**欄位名稱**去存取 tuple 的內容,
比 `t[0]`、`t[1]` 好讀很多。
from typing import NamedTuple
class Point(NamedTuple):
x: int
y: int
p = Point(x=1, y=2) # 像呼叫 class 一樣建立
print(p.x) # 用點存取 → 1
print(p[0]) # 也能用位置 → 1### 好處:不可變,安全
p = Point(x=1, y=2)
p.x = 10 # ❌ 會直接報錯:AttributeError(tuple 不能改)想「改」的話,其實是產生一個新的:
p2 = p._replace(x=10)
print(p) # Point(x=1, y=2) ← 原本的沒變
print(p2) # Point(x=10, y=2) ← 新的### 適合當函式的多值回傳
class DivResult(NamedTuple):
quotient: int
remainder: int
def divide(a: int, b: int) -> DivResult:
return DivResult(a // b, a % b)
r = divide(17, 5)
print(r.quotient, r.remainder) # 3 2
# 舊式的位置解包照樣可用
q, rem = divide(17, 5)
print(q, rem) # 3 2## 4. 一個關鍵陷阱:轉 JSON 的差別
這是實務上最容易踩到的點。
import json
from typing import NamedTuple
class Point(NamedTuple):
x: int
y: int
p = Point(x=1, y=2)
print(json.dumps(p)) # [1, 2] ← 欄位名不見了!變成陣列– `NamedTuple` → `json.dumps` 會變成 **JSON 陣列 `[1, 2]`**,**欄位名遺失**。
– `TypedDict` → `json.dumps` 會是 **JSON 物件 `{“x”: 1, “y”: 2}`**,**欄位名保留**。
👉 **只要這份資料要寫進 JSON 檔、之後還要靠鍵名讀回來,就用 `TypedDict`。**
—
## 5. 該選哪個?決策指南

**選 `TypedDict` 當:**
– 資料本來就是 dict(例如從 JSON 讀進來的)。
– 要 `json.dumps` 寫檔,且欄位名不能掉。
– 想在既有 dict 程式碼上「補上型別」,又不想改存取方式。
**選 `NamedTuple` 當:**
– 函式要一次回傳多個值。
– 資料建立後不該被修改(不可變比較安全)。
– 想用 `t.field` 的清楚寫法取代 `t[0]`、`t[1]`。
—
## 6. 並排對照

## 7. 防止「錯配」:兩者強度不一樣
很多人以為兩者防呆效果一樣,
其實**執行期強度差很多**。
關鍵在:`TypedDict` 的防護幾乎
**全靠 type checker**(靜態),
執行期它就是個普通 dict;
`NamedTuple` 因為不可變 + tuple 本質,
很多錯在**執行期就直接爆**。
### 7.1 讀取打錯欄位名
# NamedTuple:點存取打錯 → 執行期直接 AttributeError
t.flg # ❌ Python 本身就擋
# TypedDict:key 打錯 → 執行期不一定擋
d["flg"] # ⚠️ type checker 警告;執行期是 KeyError
d.get("flg") # 😶 靜默回 None,最陰險(不會報錯)### 7.2 少給/多給欄位
# NamedTuple:建構參數不對 → 執行期 TypeError
Point(1) # ❌ 缺 y,直接爆
# TypedDict:少給 key → 執行期不爆,只有 type checker 警告
p: Point = {"x": 1} # ⚠️ 少 y,靜態警告;執行期照跑### 7.3 建立後被亂改
t.x = 999 # NamedTuple:❌ 不可變,直接報錯
d["x"] = 999 # TypedDict:✅ 可隨便改,改成錯的也沒人擋### 7.4 欄位型別錯(唯一打平的情況)
Point(x="s", y=2) # NamedTuple:type checker 警告,執行期不擋
p: Point = {"x": "s", "y": 2} # TypedDict:type checker 警告,執行期不擋型別層級兩者都只靠 type checker,執行期都不驗證,效果相同。
### 防呆對照總表

👉 **論防呆最硬,`NamedTuple` 明顯更強**
(不可變 + 建構/讀取的錯執行期就爆)。
但若要「像 dict 一樣彈性 + 能存 JSON」,
還是選 `TypedDict`,代價是防護幾乎全靠 type checker。
> 補充:`collections.namedtuple`(小寫)沒有型別註解,
欄位一律當 `Any`,連 7.4 的靜態型別警告都拿不到;
> 要防呆請用 `typing.NamedTuple`(class 繼承式)。
—
## 8. 小結
– **要存 JSON、要保留鍵名 → `TypedDict`**(它就是有型別提示的 dict)。
– **要固定欄位、不可變、當函式回傳 → `NamedTuple`**(它就是有名字的 tuple)。
– 兩者都只是給人和 type checker 看的「說明書」,
讓程式更好讀、更早抓到錯。
推薦hahow線上學習python: https://igrape.net/30afN




![Python: Regular Expression 正規表示法 正則表達式 import re ; pattn = “[\d]{4}\/[01][\d]\/[0123][\d] [\d]{6}” ; match = re .search (pattn,text) .group() Python: Regular Expression 正規表示法 正則表達式 import re ; pattn = “[\d]{4}\/[01][\d]\/[0123][\d] [\d]{6}” ; match = re .search (pattn,text) .group()](https://i2.wp.com/savingking.com.tw/wp-content/uploads/2022/09/20220901154435_19.png?quality=90&zoom=2&ssl=1&resize=350%2C233)
![Python TQC考題610 平均溫度,不要自找麻煩用2D list做,可練習2D轉1D: 一維串列.extend(二維串列[index]) Python TQC考題610 平均溫度,不要自找麻煩用2D list做,可練習2D轉1D: 一維串列.extend(二維串列[index])](https://i2.wp.com/savingking.com.tw/wp-content/uploads/2022/05/20220515192908_35.png?quality=90&zoom=2&ssl=1&resize=350%2C233)




近期留言