Python dataclass(Python Dataclass)
這篇 Python dataclass 教學接續前兩篇:你已經知道一個像樣的類別要寫 __init__、__repr__、__eq__⋯⋯全是樣板。@dataclass 裝飾器讓 Python 幫你自動生成這些,特別適合「主要用來裝資料」的類別。
傳統寫法 vs dataclass
同樣的 Point 類別,兩種寫法對照:
# 傳統寫法:12 行
class PointOld:
def __init__(self, x, y):
self.x = x
self.y = y
def __repr__(self):
return f"PointOld(x={self.x}, y={self.y})"
def __eq__(self, other):
return self.x == other.x and self.y == other.y
# dataclass:4 行,功能相同
from dataclasses import dataclass
@dataclass
class Point:
x: int
y: int
p1 = Point(3, 5)
p2 = Point(3, 5)
print(p1) # 自動有 __repr__
print(p1 == p2) # 自動有 __eq__(比內容)
執行輸出:
Point(x=3, y=5)
True
x: int 這種寫法是型別提示(下一篇詳細介紹),dataclass 靠它知道有哪些欄位。
預設值
from dataclasses import dataclass
@dataclass
class Student:
name: str
score: int = 0 # 預設值
school: str = "未填寫"
s = Student("小明")
print(s)
s2 = Student("小華", score=95)
print(s2)
執行輸出:
Student(name='小明', score=0, school='未填寫')
Student(name='小華', score=95, school='未填寫')
規則跟函式參數一樣:有預設值的欄位要放在沒預設值的後面。
可變預設值:用 default_factory
List 當預設值的老問題在 dataclass 一樣存在,而且 dataclass 直接禁止你犯錯:
from dataclasses import dataclass
@dataclass
class Team:
name: str
members: list = [] # 直接報錯!
執行輸出:
ValueError: mutable default <class 'list'> for field members is not allowed: use default_factory
正確寫法用 field(default_factory=list),每個物件各自拿到新的空 List:
from dataclasses import dataclass, field
@dataclass
class Team:
name: str
members: list = field(default_factory=list)
a = Team("A 隊")
b = Team("B 隊")
a.members.append("小明")
print(a.members, b.members) # 各自獨立
執行輸出:
['小明'] []
frozen:不可變的資料類別
frozen=True 讓物件建立後不能修改(類似 Tuple 的精神),而且自動變成可雜湊、能放進 set:
from dataclasses import dataclass
@dataclass(frozen=True)
class Config:
host: str
port: int
c = Config("localhost", 8080)
print(c)
c.port = 9999 # 嘗試修改
執行輸出:
Config(host='localhost', port=8080)
dataclasses.FrozenInstanceError: cannot assign to field 'port'
常見錯誤
1. TypeError: non-default argument follows default argument
沒預設值的欄位寫在有預設值的後面。調整欄位順序:必填在前、選填在後。
2. ValueError: mutable default … use default_factory
如上節,List/dict 預設值要用 field(default_factory=list)。
3. 忘記寫型別提示
@dataclass 只認「有型別註記的類別變數」,寫 x = 0 而不是 x: int = 0,這個欄位會被忽略,建構時也不能傳。
總結
@dataclass 自動生成 __init__、__repr__、__eq__;預設值規則同函式參數、可變預設值用 default_factory、frozen=True 做不可變設定物件。資料導向的類別一律優先考慮 dataclass。它依賴的型別提示語法,下一篇完整介紹。