Python ChainMap.new_child: ซ้อน scope ใหม่โดยไม่ copy config ทั้งก้อน
config หลายระดับมักมี default, environment และ override ต่อคำขอ หาก merge ด้วย dict ทุกครั้ง เรา copy ค่าและอาจลืมว่าค่าไหนมาจากชั้นใดครับ ChainMap.new_child() เพิ่ม mapping หน้า chain ทำให้ lookup เห็น override ก่อน แต่ยังย้อนกลับไป parent ได้
WHY: ถาดงานวันนี้วางบนตู้เอกสารเดิม
เหมือนวางถาดเอกสารชั่วคราวบนตู้หลัก เวลาหาเอกสารเราดูถาดบนก่อน ถ้าไม่มีก็ค่อยเปิดลิ้นชักเดิม เมื่อจบงานยกถาดออกด้วย parents ได้โดยไม่คัดลอกทั้งตู้
from collections import ChainMap
import unittest
class ChainMapScopeTests(unittest.TestCase):
def test_new_child_overrides_without_mutating_parent(self):
defaults = {"timeout": 30, "region": "th"}
base = ChainMap(defaults)
request = base.new_child({"timeout": 5})
self.assertEqual(request["timeout"], 5)
self.assertEqual(request["region"], "th")
request["trace_id"] = "trace-1"
self.assertNotIn("trace_id", defaults)
self.assertIs(request.parents.maps[0], defaults)
def test_empty_child_accepts_local_writes(self):
base = ChainMap({"mode": "safe"})
child = base.new_child()
child["mode"] = "strict"
self.assertEqual(child["mode"], "strict")
self.assertEqual(base["mode"], "safe")
if __name__ == "__main__":
unittest.main()
เขียนลงชั้นหน้าเสมอ
การ assign/deletion ปกติกระทบ mapping แรก ไม่ได้ค้นแล้วแก้ parent ซึ่งเป็น behavior ที่ช่วยรักษา default แต่ต้องอธิบายให้ทีมเข้าใจ หากต้อง deep write ควรสร้าง abstraction ต่างหากแทนการเดา
ChainMap เป็น view สด การแก้ mapping ต้นทางจะสะท้อนใน lookup ภายหลัง จึงไม่ใช่ snapshot สำหรับ audit หรือ async job ที่ต้อง reproducible ให้ materialize เป็น dict(chain) ที่ boundary และ redact secret ก่อน log
Recap
- ใช้
new_childสำหรับ scope/override ชั่วคราว - เขียนลง mapping แรกโดยตั้งใจ
- ใช้
parentsเพื่อกลับ scope เดิม - สร้าง snapshot เมื่อความทำซ้ำได้สำคัญกว่า live view