Python 資料模型完整教學:dataclass、TypedDict 與 Pydantic 的選擇與比較; from dataclasses import dataclass #自訂類別物件; from typing import TypedDict #內建的dict物件; from pydantic import BaseModel #自訂模型物件

加入好友
加入社群
Python 資料模型完整教學:dataclass、TypedDict 與 Pydantic 的選擇與比較; from dataclasses import dataclass #自訂類別物件; from typing import TypedDict #內建的dict物件; from pydantic import BaseModel #自訂模型物件 - 儲蓄保險王

在 Python 開發中,
我們經常需要描述一組固定結構的資料,
例如:

  • 使用者資料
  • API 請求與回應
  • 設定檔
  • 測試結果
  • 資料庫紀錄
  • JSON 文件

Python 提供多種方式處理這類資料,其中最常見的包括:

@dataclass
TypedDict
Pydantic BaseModel

from dataclasses import dataclass  
from typing import TypedDict
from pydantic import BaseModel

這三種工具看起來都可以定義欄位與型別,
但實際用途並不相同。


一、先看三者的核心差異

Python 資料模型完整教學:dataclass、TypedDict 與 Pydantic 的選擇與比較; from dataclasses import dataclass #自訂類別物件; from typing import TypedDict #內建的dict物件; from pydantic import BaseModel #自訂模型物件 - 儲蓄保險王

看表前先記住三件事(細節後面各章都會展開):

  1. @dataclass 與 pydantic.BaseModel 是「同類」:兩者都是 Python 類別,實例都是物件, 存取方式都是 user.name。差別不在「是不是物件」,而在 pydantic.BaseModel 多了執行期驗證
  2. TypedDict 不是新的執行期型別:它只是「加在字典上的靜態契約」,只存在於型別檢查器 (mypy / Pyright)眼中;你實際拿在手上的資料,執行期就是一個普通 dict
  3. 命名層級不同@dataclassTypedDict 是標準庫的語言構造名;而「Pydantic」是第三方套件名, 你實際 import 並繼承的類別是 pydantic.BaseModelfrom pydantic import BaseModel class User(BaseModel): # ← 對應 @dataclass / TypedDict 的具體構造 name: str

(本文描述「模型/構造」時一律用 pydantic.BaseModel,只在指套件本身,或其版本世代 〔Pydantic 1.x / 2.x,本文以 v1 / v2 簡稱〕時才用「Pydantic」。)

可以先用三句話理解:

TypedDict:描述「這個 dict 應該有哪些欄位」的靜態型別標註。
dataclass:這是一個具有欄位的 Python 物件。
pydantic.BaseModel:這是一個會驗證輸入資料的 Python 物件。

二、dataclass:建立簡潔的資料物件

1. 傳統 Python 類別的寫法

如果不用 dataclass,可能要手動撰寫 __init__

class User:
    def __init__(self, name: str, age: int):
        self.name = name
        self.age = age

建立物件:

user = User(
    name="Alice",
    age=30
)

這種寫法沒有錯,但欄位多時會產生大量重複程式碼。


2. 使用 dataclass

from dataclasses import dataclass


@dataclass
class User:
    name: str
    age: int

Python 會自動幫你產生初始化方法:

user = User(
    name="Alice",
    age=30
)

print(user)
# 輸出User(name='Alice', age=30)

也會自動提供適合的:

  • __init__
  • __repr__
  • __eq__

因此,dataclass 非常適合用來表示「主要由資料組成」的類別。


3. dataclass 的型別標註不是驗證

這是非常重要的觀念:

from dataclasses import dataclass


@dataclass
class User:
    name: str
    age: int

雖然 age 標註為 int,但 Python 預設不會阻止錯誤型別:

user = User(
    name="Alice",
    age="thirty"
)

print(user.age)
Python 資料模型完整教學:dataclass、TypedDict 與 Pydantic 的選擇與比較; from dataclasses import dataclass #自訂類別物件; from typing import TypedDict #內建的dict物件; from pydantic import BaseModel #自訂模型物件 - 儲蓄保險王

也就是說:

dataclass 的型別標註主要提供給 IDE、mypy、Pyright 等
靜態分析工具使用,預設不會在執行時驗證資料。


4. dataclass 的預設值

from dataclasses import dataclass


@dataclass
class User:
    name: str
    age: int = 0

使用:

user = User(name="Alice")

print(user.age)
# 輸出0

5. 可變預設值要使用 default_factory

無法直接這樣寫:

from dataclasses import dataclass


@dataclass
class User:
    tags: list[str] = []
# ValueError: mutable default <class 'list'> for field tags is not allowed: 
# use default_factory

⚠️ 這段程式根本無法載入@dataclass 在類別定義的當下就會直接丟出 ValueError

ValueError: mutable default <class 'list'> for field tags is not allowed: use default_factory

它擋下的判斷依據是「不可雜湊(unhashable)的預設值」,而 listdictset 正好都不可雜湊(不可雜湊通常就代表可變物件),所以拿它們當預設值一定會被擋下。

作為對照,下面是傳統類別中真正會發生「被共用」的錯誤寫法
dataclass 正是為了擋住這種陷阱,才直接禁止不可雜湊預設值):

class BadUser:
    tags: list[str] = []  # 類別屬性所有實例共用同一個 list


a = BadUser()
b = BadUser()
a.tags.append("Python")
print(b.tags)  # ['Python'] ← b 也被影響了這才是共用陷阱
Python 資料模型完整教學:dataclass、TypedDict 與 Pydantic 的選擇與比較; from dataclasses import dataclass #自訂類別物件; from typing import TypedDict #內建的dict物件; from pydantic import BaseModel #自訂模型物件 - 儲蓄保險王

dataclass 的正確寫法:

from dataclasses import dataclass, field


@dataclass
class User:
    name: str
    tags: list[str] = field(default_factory=list)

每次建立 User 時,都會建立一個新的清單:

user1 = User(name="Alice")
user2 = User(name="Bob")

user1.tags.append("Python")

print(user1.tags)
print(user2.tags)
Python 資料模型完整教學:dataclass、TypedDict 與 Pydantic 的選擇與比較; from dataclasses import dataclass #自訂類別物件; from typing import TypedDict #內建的dict物件; from pydantic import BaseModel #自訂模型物件 - 儲蓄保險王

除了 list,也可以使用:

field(default_factory=dict)
field(default_factory=set)

或自己的函式:

def default_tags() -> list[str]:
    return ["new"]


@dataclass
class User:
    tags: list[str] = field(
        default_factory=default_tags
    )
Python 資料模型完整教學:dataclass、TypedDict 與 Pydantic 的選擇與比較; from dataclasses import dataclass #自訂類別物件; from typing import TypedDict #內建的dict物件; from pydantic import BaseModel #自訂模型物件 - 儲蓄保險王

6. dataclass 可以包含方法

dataclass 不只是資料容器,也可以包含商業邏輯或操作方法:

from dataclasses import dataclass


@dataclass
class User:
    name: str
    age: int

    def is_adult(self) -> bool:
        return self.age >= 18

使用:

user = User(
    name="Alice",
    age=30
)

print(user.is_adult())
Python 資料模型完整教學:dataclass、TypedDict 與 Pydantic 的選擇與比較; from dataclasses import dataclass #自訂類別物件; from typing import TypedDict #內建的dict物件; from pydantic import BaseModel #自訂模型物件 - 儲蓄保險王

這是 dataclass 與 TypedDict 的重要差異之一。


7. 不可變的 dataclass

如果希望物件建立後不能修改,可以使用 frozen=True

from dataclasses import dataclass


@dataclass(frozen=True)
class Point:
    x: int
    y: int

以下操作會失敗:

point = Point(10, 20)
point.x = 100
# FrozenInstanceError: cannot assign to field 'x'

因為 frozen dataclass 不允許修改欄位,
會丟出 FrozenInstanceError: cannot assign to field ‘x’

frozen=True 擋下的不只是「修改既有欄位」——它會攔截所有對實例的屬性設定與刪除:

Python 資料模型完整教學:dataclass、TypedDict 與 Pydantic 的選擇與比較; from dataclasses import dataclass #自訂類別物件; from typing import TypedDict #內建的dict物件; from pydantic import BaseModel #自訂模型物件 - 儲蓄保險王

適合用於:

  • 座標
  • 設定值
  • 常數資料
  • 希望具有不可變特性的值物件

三、TypedDict:描述字典的結構

1. TypedDict 是什麼?

TypedDict 用來描述一個字典應該包含哪些 key,
以及每個 key 的資料型別。
它是一種靜態型別標註——只在型別檢查器眼中生效,
執行期並不會建立新的資料型別,你操作的資料仍然是普通 dict

from typing import TypedDict


class UserDict(TypedDict):
    name: str
    age: int

建立資料:

user: UserDict = {
    "name": "Alice",
    "age": 30
}

存取資料:

print(user["name"])
print(user["age"])
Python 資料模型完整教學:dataclass、TypedDict 與 Pydantic 的選擇與比較; from dataclasses import dataclass #自訂類別物件; from typing import TypedDict #內建的dict物件; from pydantic import BaseModel #自訂模型物件 - 儲蓄保險王

2. TypedDict 實際上仍然是普通字典

print(type(user))
# <class 'dict'>

TypedDict 的實例就是普通的 dict,執行期不會產生一個獨立的類別實例。它主要提供的是型別提示,讓 IDE 與靜態分析工具知道這個字典應該長什麼樣子。

一個容易誤會的細節:連用「建構子」建出來的也是純 dict

即使你把 UserDict 當成類別來呼叫,得到的仍然是普通 dict,而不是一個 UserDict 實例;而且執行期完全不會驗證欄位型別:

from typing import TypedDict


class UserDict(TypedDict):
    name: str
    age: int


u = UserDict(name=123, age="thirty")   # 故意給錯型別

print(type(u))          # <class 'dict'>  ← 不是 UserDict
print(u)                # {'name': 123, 'age': 'thirty'}執行期照收不誤
Python 資料模型完整教學:dataclass、TypedDict 與 Pydantic 的選擇與比較; from dataclasses import dataclass #自訂類別物件; from typing import TypedDict #內建的dict物件; from pydantic import BaseModel #自訂模型物件 - 儲蓄保險王

UserDict.__mro__ 雖然顯示 (UserDict, dict, object),看似 dict 的子類, 但那只是 typing 的特殊構造;UserDict(...) 建出來的物件其 type() 永遠是 dict。 換句話說,TypedDict 的角色是「靜態描述」,不是「執行期型別」。

3. TypedDict 不會自動驗證

from typing import TypedDict


class UserDict(TypedDict):
    name: str
    age: int


user: UserDict = {
    "name": 123,
    "age": "thirty"
}

Python 預設不會立刻報錯。

但是,如果使用 mypy 或 Pyright,
靜態檢查工具可能會提出錯誤:

name 應該是 str,但目前是 int
age 應該是 int,但目前是 str

因此:

TypedDict 是靜態型別提示,不是執行時資料驗證工具。


4. 必填欄位與非必填欄位

預設情況下,TypedDict 的欄位都是必填:

from typing import TypedDict


class UserDict(TypedDict):
    name: str
    age: int

以下資料不完整:

user: UserDict = {
    "name": "Alice"
}

靜態分析工具會指出缺少 age

如果某些欄位可以省略,可以使用 NotRequired

from typing import NotRequired, TypedDict


class UserDict(TypedDict):
    name: str
    age: int
    email: NotRequired[str]

此時以下資料是合法結構:

user: UserDict = {
    "name": "Alice",
    "age": 30
}

5. 全部欄位都可以省略

可以使用:

# %%
from typing import TypedDict


class UserUpdate(TypedDict, total=False):
    name: str
    age: int
    email: str

這代表所有欄位都是可選的:

# %%
update: UserUpdate = {
    "email": "alice@example.com"
}
Python 資料模型完整教學:dataclass、TypedDict 與 Pydantic 的選擇與比較; from dataclasses import dataclass #自訂類別物件; from typing import TypedDict #內建的dict物件; from pydantic import BaseModel #自訂模型物件 - 儲蓄保險王

6. TypedDict 適合 JSON

JSON 的本質就是字典、清單、字串、數字、布林值與 null

因此,以下資料很適合用 TypedDict 描述:

from typing import TypedDict


class ApiResponse(TypedDict):
    success: bool
    message: str
    data: dict

使用:

response: ApiResponse = {
    "success": True,
    "message": "OK",
    "data": {}
}

適合的場景包括:

  • API 回應
  • 設定資料
  • JSON 文件
  • 字典格式的查詢結果
  • 不需要方法的資料結構

四、pydantic.BaseModel:具有執行時驗證的資料模型

1. 基本用法

先安裝:

pip install pydantic

建立模型:

from pydantic import BaseModel


class User(BaseModel):
    name: str
    age: int

使用:

user = User(
    name="Alice",
    age=30
)

print(user.name)
print(user.age)
Python 資料模型完整教學:dataclass、TypedDict 與 Pydantic 的選擇與比較; from dataclasses import dataclass #自訂類別物件; from typing import TypedDict #內建的dict物件; from pydantic import BaseModel #自訂模型物件 - 儲蓄保險王

pydantic.BaseModel 的物件存取方式和 dataclass 類似:

user.name

2. pydantic.BaseModel 會驗證資料

from pydantic import BaseModel


class User(BaseModel):
    name: str
    age: int


user = User(
    name="Alice",
    age="thirty"
)
#ValidationError: 1 validation error for User
age
  Input should be a valid integer, unable to parse string as an integer [type=int_parsing, input_value='thirty', input_type=str]
    For further information visit https://errors.pydantic.dev/2.11/v/int_parsing

因為 "thirty" 無法轉換成整數,
pydantic.BaseModel 會產生 ValidationError

ValidationError: 1 validation error for User
age
  Input should be a valid integer, unable to parse string as an integer [type=int_parsing, input_value='thirty', input_type=str]
    For further information visit https://errors.pydantic.dev/2.11/v/int_parsing

實際使用時可以捕捉錯誤:

from pydantic import ValidationError
try:
    user = User(
        name="Alice",
        age="thirty"
    )
except ValidationError as error:
    print(error)

3. pydantic.BaseModel 可能會自動轉換型別

class User(BaseModel):
    name: str
    age: int


user = User(
    name="Alice",
    age="30"
)

print(user.age)
print(type(user.age))

pydantic.BaseModel 通常會將:

"30"

轉換為:

30
Python 資料模型完整教學:dataclass、TypedDict 與 Pydantic 的選擇與比較; from dataclasses import dataclass #自訂類別物件; from typing import TypedDict #內建的dict物件; from pydantic import BaseModel #自訂模型物件 - 儲蓄保險王

因此,pydantic.BaseModel 預設通常是「驗證並進行合理轉換」,不一定是完全禁止型別轉換。


4. 使用 Strict 型別

如果希望嚴格要求實際型別,可以使用:

from pydantic import BaseModel, StrictInt


class User(BaseModel):
    name: str
    age: StrictInt

此時:

user = User(
    name="Alice",
    age="30"
)
# ValidationError: 1 validation error for User
age
  Input should be a valid integer [type=int_type, input_value='30', input_type=str]
    For further information visit https://errors.pydantic.dev/2.11/v/int_type

會驗證失敗,因為 "30" 是字串,不是整數。

常見 Strict 型別包括:

StrictStr
StrictInt
StrictFloat
StrictBool

範例:

from pydantic import BaseModel
from pydantic import StrictBool, StrictInt, StrictStr


class Settings(BaseModel):
    name: StrictStr
    retry_count: StrictInt
    enabled: StrictBool

5. pydantic.BaseModel 的預設值

from pydantic import BaseModel, Field


class User(BaseModel):
    name: str
    age: int = 0
    tags: list[str] = Field(default_factory=list)

Field(default_factory=list) 和 dataclass 的:

from dataclasses import dataclass, field
field(default_factory=list)

概念相近,都是為了確保每個物件取得獨立的清單。

pydantic.Field(default_factory=list)
大寫 Field,來自 from pydantic import Field) 和
dataclass 的 dataclasses.field(default_factory=list)
小寫 field,來自 from dataclasses import field) 概念相近,
都是為了確保每個物件取得獨立的清單。

注意大小寫與來源不同:
pydantic.Field 是大寫 F、來自 pydantic
dataclasses.field 是小寫 f、來自 dataclasses
兩者名字像、用途類似,但不能互換 import。


6. pydantic.BaseModel 的巢狀模型

from pydantic import BaseModel


class Address(BaseModel):
    city: str
    country: str


class User(BaseModel):
    name: str
    age: int
    address: Address

建立資料:

user = User(
    name="Alice",
    age=30,
    address={
        "city": "Taipei",
        "country": "Taiwan"
    }
)

Pydantic 可以將巢狀字典轉換為 Address 物件。

Python 資料模型完整教學:dataclass、TypedDict 與 Pydantic 的選擇與比較; from dataclasses import dataclass #自訂類別物件; from typing import TypedDict #內建的dict物件; from pydantic import BaseModel #自訂模型物件 - 儲蓄保險王

7. 未宣告的額外欄位:預設會被「忽略」

這是使用 pydantic.BaseModel 做外部輸入驗證時很容易忽略的一點:Pydantic v2 預設會直接忽略未宣告的額外欄位,不會報錯

from pydantic import BaseModel


class User(BaseModel):
    name: str


user = User(name="Alice", extra_field="dropped")

print(user.model_dump())

輸出(額外欄位被丟掉,但不會報錯):

Python 資料模型完整教學:dataclass、TypedDict 與 Pydantic 的選擇與比較; from dataclasses import dataclass #自訂類別物件; from typing import TypedDict #內建的dict物件; from pydantic import BaseModel #自訂模型物件 - 儲蓄保險王

如果你的目標是外部輸入的嚴格 schema(例如 API 請求、設定檔),通常會希望「收到未知欄位就直接拒絕」,此時要設定 extra="forbid"

from pydantic import BaseModel, ConfigDict, ValidationError


class User(BaseModel):
    model_config = ConfigDict(extra="forbid")
    name: str


try:
    User(name="Alice", extra_field="x")
except ValidationError as error:
    print("拒絕未知欄位:", error)

extra 有三種常見設定:

Python 資料模型完整教學:dataclass、TypedDict 與 Pydantic 的選擇與比較; from dataclasses import dataclass #自訂類別物件; from typing import TypedDict #內建的dict物件; from pydantic import BaseModel #自訂模型物件 - 儲蓄保險王

五、三者實際比較

假設要描述相同的使用者資料。

1. 使用 dataclass

from dataclasses import dataclass


@dataclass
class User:
    name: str
    age: int

建立:

user = User(
    name="Alice",
    age=30
)

存取:

user.name

特點:

  • 是真正的 Python 類別物件。
  • 可以加入方法。
  • 預設不驗證型別。
  • 適合程式內部使用。

2. 使用 TypedDict

from typing import TypedDict


class UserDict(TypedDict):
    name: str
    age: int

建立:

user: UserDict = {
    "name": "Alice",
    "age": 30
}

存取:

user["name"]

特點:

  • 實際上是普通字典。
  • 適合 JSON 與 API 資料。
  • 不會執行時驗證。
  • 不適合定義物件行為。

3. 使用 pydantic.BaseModel

from pydantic import BaseModel


class User(BaseModel):
    name: str
    age: int

建立:

user = User(
    name="Alice",
    age=30
)

存取:

user.name

特點:

  • 是資料模型物件。
  • 會在執行時驗證。
  • 適合外部輸入與 API。
  • 支援 JSON 解析與輸出。

六、資料輸出與 JSON 處理

dataclass 轉字典

from dataclasses import asdict, dataclass


@dataclass
class User:
    name: str
    age: int


user = User(
    name="Alice",
    age=30
)

data = asdict(user)

print(data)

輸出:

Python 資料模型完整教學:dataclass、TypedDict 與 Pydantic 的選擇與比較; from dataclasses import dataclass #自訂類別物件; from typing import TypedDict #內建的dict物件; from pydantic import BaseModel #自訂模型物件 - 儲蓄保險王

如果要轉 JSON:

import json

json_text = json.dumps(
    asdict(user),
    ensure_ascii=False
)

print(json_text)

TypedDict 本身就是字典

from typing import TypedDict


class UserDict(TypedDict):
    name: str
    age: int


user: UserDict = {
    "name": "Alice",
    "age": 30
}

可以直接使用:

import json

json_text = json.dumps(
    user,
    ensure_ascii=False
)

pydantic.BaseModel 轉字典與 JSON

Pydantic 版本 2 使用:

from pydantic import BaseModel


class User(BaseModel):
    name: str
    age: int


user = User(
    name="Alice",
    age=30
)

data = user.model_dump()
json_text = user.model_dump_json()

全新 pip install pydantic 預設就是 v2(2.x);只有在被 pin 舊版或維護既有 v1 專案時,才會用到下面的 dict() / json() 寫法。

如果是 Pydantic 版本 1,常見寫法是:

user.dict()
user.json()

注意:.dict() / .json() 是 v1 的主要寫法;在 v2 它們仍可用但已 deprecated——呼叫時會發出 PydanticDeprecatedSince20 警告(屬 DeprecationWarning),並預計在未來 v3 移除。功能上等同 model_dump() / model_dump_json(),所以新程式請直接用新 API

但新程式建議優先使用 Pydantic 2 的:

model_dump()
model_dump_json()

七、如何選擇?

選擇 dataclass 的時機

適合:

  • 資料主要在程式內部流動。
  • 需要使用 obj.field
  • 需要加入方法。
  • 需要自訂初始化或比較行為。
  • 不需要自動驗證外部資料。
  • 希望依賴 Python 標準函式庫。

範例:

from dataclasses import dataclass


@dataclass
class Point:
    x: int
    y: int

    def distance_to_origin(self) -> float:
        return (self.x ** 2 + self.y ** 2) ** 0.5

選擇 TypedDict 的時機

適合:

  • 資料本質上就是字典(例如 JSON、API 回應、設定檔)。
  • 需要用 data["key"] 的方式存取。
  • 只想要靜態型別提示,不需要執行時驗證。
  • 不需要在資料上掛方法或行為。
  • 想避免額外的物件轉換成本(因為它本身就是 dict)。

範例:

from typing import TypedDict


class ApiResponse(TypedDict):
    success: bool
    message: str
    data: dict

選擇 pydantic.BaseModel 的時機

適合:

  • 資料來自外部且不可信(使用者輸入、API 請求、設定檔)。
  • 需要執行時驗證,確保型別與格式正確。
  • 需要自動型別轉換或嚴格型別檢查。
  • 需要巢狀模型、序列化與反序列化。
  • 建構 Web API(例如 FastAPI)。

範例:

from pydantic import BaseModel, StrictInt


class User(BaseModel):
    name: str
    age: StrictInt

八、一句話總結

Python 資料模型完整教學:dataclass、TypedDict 與 Pydantic 的選擇與比較; from dataclasses import dataclass #自訂類別物件; from typing import TypedDict #內建的dict物件; from pydantic import BaseModel #自訂模型物件 - 儲蓄保險王

核心判斷邏輯:

資料可信在程式內流動dataclass
資料本身就是字典 / JSONTypedDict
資料來自外部必須驗證pydantic.BaseModel

附錄:常見誤區速查

Python 資料模型完整教學:dataclass、TypedDict 與 Pydantic 的選擇與比較; from dataclasses import dataclass #自訂類別物件; from typing import TypedDict #內建的dict物件; from pydantic import BaseModel #自訂模型物件 - 儲蓄保險王

Pydantic 這個名字的由來與發音

由來:Python + pedantic

Pydantic 不是單一英文單字的變形,而是一個組合字

Py(取自 Python) + dantic(取自 pedantic 後半) = Pydantic

pedantic 是什麼意思?

pedantic(形容詞)指:

  • 過度注重細節的
  • 過度拘泥規則的
  • 學究式的、吹毛求疵的

例句:

He is very pedantic about grammar.
(他對文法非常講究,甚至有點過度拘泥細節。)

在日常英文中 pedantic 常帶負面意味(太龜毛);但用在 Pydantic 的命名上是正面的,強調「嚴謹、精確、重視資料格式、不接受錯誤輸入」——正好對應它「嚴格驗證 Python 資料」的設計理念。

發音

Python 資料模型完整教學:dataclass、TypedDict 與 Pydantic 的選擇與比較; from dataclasses import dataclass #自訂類別物件; from typing import TypedDict #內建的dict物件; from pydantic import BaseModel #自訂模型物件 - 儲蓄保險王

兩者重音都在中間的 DAN;差別只在開頭:pedantic 是「皮」、Pydantic 是「派」(因為 Py 唸成 pie)。

一句話記憶

Python + pedantic = Pydantic
(Python + 對資料細節很講究 → 派丹提克)

推薦hahow線上學習python: https://igrape.net/30afN

加入好友
加入社群
Python 資料模型完整教學:dataclass、TypedDict 與 Pydantic 的選擇與比較; from dataclasses import dataclass #自訂類別物件; from typing import TypedDict #內建的dict物件; from pydantic import BaseModel #自訂模型物件 - 儲蓄保險王

儲蓄保險王

儲蓄險是板主最喜愛的儲蓄工具,最喜愛的投資理財工具則是ETF,最喜愛的省錢工具則是信用卡

You may also like...

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *