Python deque(maxlen): เก็บประวัติล่าสุดโดยไม่ปล่อยหน่วยความจำโตไม่จบ
รายการ log ล่าสุด, ค่า latency รอบสั้น หรือคำสั่ง undo มักต้องเก็บเพียง N รายการ การใช้ list แล้วคอยเช็กความยาวและ pop(0) ทำให้กฎขอบเขตกระจายอยู่หลายจุด และการลบหัว list ต้องเลื่อนสมาชิกที่เหลือ collections.deque ที่กำหนด maxlen จะทิ้งสมาชิกฝั่งตรงข้ามให้อัตโนมัติเมื่อเต็มครับ
from collections import deque
import unittest
class RecentEvents:
def __init__(self, capacity: int) -> None:
if capacity <= 0:
raise ValueError("capacity must be positive")
self._items: deque[str] = deque(maxlen=capacity)
def record(self, event: str) -> None:
self._items.append(event)
def snapshot(self) -> tuple[str, ...]:
return tuple(self._items)
class RecentEventsTests(unittest.TestCase):
def test_discards_oldest_when_full(self):
history = RecentEvents(3)
for event in ("open", "edit", "save", "close"):
history.record(event)
self.assertEqual(history.snapshot(), ("edit", "save", "close"))
def test_rejects_zero_capacity(self):
with self.assertRaises(ValueError):
RecentEvents(0)
if __name__ == "__main__":
unittest.main()
maxlen เป็น immutable หลังสร้าง ถ้าต้องเปลี่ยนขนาดให้สร้าง deque ใหม่จากข้อมูลเดิม และต้องตกลงให้ชัดว่าเมื่อเต็มจะทิ้งข้อมูลเงียบ ๆ ได้จริงหรือไม่ สำหรับ audit log หรือธุรกรรม การทิ้งอัตโนมัติเป็นพฤติกรรมผิด ควรส่งไป durable storage และให้ backpressure แทน
การอ่าน index ตรงกลาง deque ไม่ใช่จุดแข็ง เพราะจะช้าลงเมื่อไกลจากปลายทั้งสอง ถ้างานเน้น random access ใช้ list ยังเหมาะกว่า แต่ถ้าเป็น rolling window ที่เพิ่มและนำออกจากหัวท้าย deque(maxlen=N) ทำให้ memory bound เป็นส่วนหนึ่งของโครงสร้าง ไม่ใช่คำสัญญาใน comment
Recap
- ใช้
maxlenเมื่อยอมทิ้งรายการเก่าสุดได้ - validate capacity ก่อนสร้าง
- คืน snapshot แทนเปิด deque ภายในให้แก้ตรง ๆ
- อย่าใช้ rolling buffer แทน audit log ถาวร