งูต้นฉบับมองช่องแผนที่สีเขียวผ่านฝาครอบใสที่ล็อกการแตะ แต่ข้อมูลด้านในยังเปลี่ยนตามเจ้าของ

Python MappingProxyType: แจก View อ่านอย่างเดียวโดยไม่แช่แข็งข้อมูลต้นทาง

พี่โชว์Engineering

คลาสที่คืน dict ภายในตรง ๆ เท่ากับยื่นกุญแจห้องเก็บของให้ caller: ใครก็แก้ค่าได้ แต่การคืนสำเนาทุกครั้งมีต้นทุนและทำให้ผู้เรียกไม่เห็นสถานะใหม่ types.MappingProxyType เป็นฝาครอบอ่านอย่างเดียวเหนือ mapping เดิม จึงห้ามเขียนผ่าน view แต่สะท้อนการเปลี่ยนที่ owner ทำครับ

import unittest
from types import MappingProxyType


class FeatureRegistry:
    def __init__(self) -> None:
        self._flags = {"search": False}
        self.flags = MappingProxyType(self._flags)

    def enable(self, name: str) -> None:
        self._flags[name] = True


class MappingProxyTests(unittest.TestCase):
    def test_view_rejects_write_but_tracks_owner_updates(self):
        registry = FeatureRegistry()
        with self.assertRaises(TypeError):
            registry.flags["search"] = True

        registry.enable("search")
        self.assertTrue(registry.flags["search"])

    def test_missing_key_keeps_mapping_semantics(self):
        registry = FeatureRegistry()
        with self.assertRaises(KeyError):
            _ = registry.flags["payments"]


if __name__ == "__main__":
    unittest.main()

Read-only ไม่เท่ากับ Deeply Immutable

proxy ป้องกันการเพิ่ม ลบ หรือแทนค่าผ่านตัวมันเอง แต่ถ้าค่าเป็น list หรือ object ที่แก้ไขได้ caller ยังอาจเปลี่ยนของข้างใน เช่น view["roles"].append(...) ถ้าต้องการ snapshot immutable จริง ให้แปลงค่าซ้อนเป็น tuple/frozen dataclass หรือ deep copy ตาม boundary

เพราะ view เป็นแบบ dynamic การอ่านสองครั้งอาจได้ผลต่างกันถ้า owner แก้ mapping ระหว่างนั้น ใน code concurrent ควรกำหนด lock หรือ copy snapshot ก่อนทำ operation หลายขั้น อย่าเข้าใจว่า proxy เป็น synchronization primitive

ข้อดีคือ API สื่อ ownership ชัด: consumer อ่าน ส่วน registry เป็นคนเขียน เหมาะกับ configuration registry, dispatch table และ metadata ที่ต้องอัปเดตโดยเจ้าของเดียว หาก consumer ต้องเก็บหลักฐาน ณ เวลาใดเวลาหนึ่ง ให้คืน dict(proxy) แทนและตั้งชื่อว่า snapshot เพื่อไม่ทำให้คนสับสน

Recap

  • proxy ห้ามเขียนผ่าน view แต่ยังติดตาม mapping ต้นทาง
  • mutable value ด้านในยังแก้ได้ ต้อง freeze แยก
  • ใช้ lock/snapshot เมื่อต้องอ่านหลายขั้นแบบสอดคล้องกัน
  • เลือก view หรือ snapshot ให้ตรง semantics ของ API ครับ