Python dataclasses.astuple: แปลงเป็น tuple พร้อมรู้ต้นทุน deep copy
เวลาส่ง dataclass เข้า API ที่ต้องการ tuple เราอาจคิดว่า dataclasses.astuple แค่หยิบ field มาเรียง แต่ฟังก์ชันนี้เดินข้อมูลซ้อนและคัดลอกวัตถุอื่นด้วย deepcopy ครับ ผลลัพธ์จึงแยกจากต้นฉบับ แต่มีต้นทุนที่ต้องรู้
WHY: snapshot ปลอดการแก้ร่วม แต่ไม่ฟรี
เหมือนถ่ายสำเนาแฟ้มทุกซองก่อนส่งออก ไม่ใช่แค่เขียนรายการชื่อแฟ้ม ถ้าต้นฉบับเปลี่ยนภายหลัง snapshot ไม่เปลี่ยนตาม เหมาะกับ boundary บางจุด แต่ object graph ใหญ่หรือมี resource ที่คัดลอกไม่ได้อาจช้าและล้มเหลว
import unittest
from dataclasses import astuple, dataclass
@dataclass
class Job:
name: str
tags: list[str]
class AsTupleTests(unittest.TestCase):
def test_nested_list_is_copied(self):
tags = ["daily", "safe"]
job = Job("publish", tags)
snapshot = astuple(job)
tags.append("changed")
self.assertEqual(snapshot, ("publish", ["daily", "safe"]))
self.assertEqual(job.tags, ["daily", "safe", "changed"])
def test_custom_tuple_factory(self):
job = Job("verify", ["content"])
snapshot = astuple(job, tuple_factory=lambda values: tuple(values))
self.assertEqual(snapshot[0], "verify")
if __name__ == "__main__":
unittest.main()
ถ้าต้องการ shallow tuple ตาม field จริง ๆ ใช้ tuple(getattr(obj, field.name) for field in dataclasses.fields(obj)) แล้วเขียนชื่อให้ชัดว่า shallow การเลือกขึ้นกับ contract: snapshot แยกขาดใช้ astuple; view เบา ๆ ที่ยอมแชร์ object ใช้ shallow conversion
Recap
-
astupleเดิน dataclass, dict, list และ tuple แบบ recursive - วัตถุอื่นถูกส่งผ่าน
deepcopy - เหมาะกับ snapshot ที่ boundary มากกว่าการเรียกใน hot loop
- เขียน shallow conversion เองเมื่อเจตนาคือแชร์ object ซ้อน