Python 3.14 max-heap API: เลิกใส่เครื่องหมายลบเพื่อจัดคิวค่าสูงสุด
ก่อน Python 3.14 คนทำ max-heap มักเก็บตัวเลขติดลบใน min-heap ครับ เทคนิคนี้ใช้ได้ แต่ทำให้ domain value กับ storage value คนละความหมาย และพลาดง่ายเมื่อ tuple มีหลาย field Python 3.14 เพิ่ม heapify_max, heappush_max, heappop_max และคู่ replace/pushpop ให้เรียกตรงเจตนา
WHY: ลบ mental translation ออกจากโค้ด
ถ้าคะแนนจริงคือ 90 แต่ใน heap เก็บ -90 คนอ่านต้องพลิกป้ายในหัวทุกครั้ง เหมือนลิฟต์เขียนชั้นบนสุดเป็นเลขติดลบ ยิ่งมี priority, timestamp และ id ใน tuple กฎ tie-break ยิ่งอ่านยาก API max-heap ทำให้ข้อมูลใน list ยังเป็นค่าจริง และ root คือค่าสูงสุด
HOW: รักษา top priority ด้วย API ชุดเดียว
import heapq
import unittest
class MaxHeapTests(unittest.TestCase):
def test_highest_priority_is_popped_first(self) -> None:
priorities = [30, 10, 80, 50]
heapq.heapify_max(priorities)
heapq.heappush_max(priorities, 70)
popped = [heapq.heappop_max(priorities) for _ in range(3)]
self.assertEqual(popped, [80, 70, 50])
def test_pushpop_keeps_new_top(self) -> None:
priorities = [90, 60, 40]
heapq.heapify_max(priorities)
removed = heapq.heappushpop_max(priorities, 50)
self.assertEqual(removed, 90)
self.assertEqual(priorities[0], 60)
if __name__ == "__main__":
unittest.main()
อย่าผสมฟังก์ชัน min-heap กับ max-heap บน list เดียวกัน invariant หรือกฎการจัดกองจะเสียโดยไม่จำเป็นต้อง error ทันที ตั้งชื่อ helper หรือห่อ heap ใน class เล็ก ๆ เมื่อมีหลายทีมแตะข้อมูลชุดเดียวกัน
เลือก operation ให้ตรงงาน
heappushpop_max ใส่ค่าใหม่แล้วคืนค่าสูงสุดอย่างมีประสิทธิภาพ ส่วน heapreplace_max เอา root เดิมออกก่อนแล้วใส่ค่าใหม่ ความต่างสำคัญเมื่อค่าใหม่สูงกว่า root เดิม จึงเขียน test ที่ครอบสองด้านของ threshold
ถ้าต้องการคิวงาน concurrent heapq ไม่ใส่ lock ให้เอง ให้ใช้ queue.PriorityQueue หรือจัด synchronization รอบ list และจำไว้ว่า max-heap API ต้องใช้ Python 3.14 ขึ้นไป โปรเจกต์ที่รองรับรุ่นเก่าต้องคง compatibility layer ชัดเจน
Recap
- ใช้ max-heap API เพื่อเก็บค่าจริง ไม่ต้อง negate
- ห้ามผสม min/max operations ใน heap เดียว
- แยก
pushpopกับreplaceด้วย test - ใส่ lock หรือใช้ queue เมื่อมีหลาย thread