งูต้นฉบับเทเมล็ดจากถาดหลายสีลงตาชั่งเดียว โดยมีถาดอำพันหนึ่งใบแสดงยอดที่ต้องหักคืน

Python Counter.total: รวมจำนวนคงเหลือโดยยังเห็นค่าติดลบ

พี่โชว์Engineering

Counter ไม่ได้เก็บเฉพาะจำนวนบวก มันรองรับศูนย์และค่าติดลบสำหรับการคืนของหรือปรับยอดด้วย Counter.total() รวม count ทุก key ตามที่เก็บอยู่ จึงเหมาะกับยอดสุทธิ แต่ต้องเข้าใจว่าไม่ได้ตัดค่าติดลบทิ้งครับ

WHY: รวมสมุดรับเข้าและคืนของเล่มเดียว

ร้านหนึ่งมีรับของ 12 ชิ้นและคืนผู้ขาย 2 ชิ้น ยอดสุทธิคือ 10 ไม่ใช่ 12 การใช้ sum(counter.values()) ได้คำตอบเดียวกัน แต่ total() สื่อเจตนาว่าเรากำลังรวม counter และอ่านง่ายกว่าใน code review

from collections import Counter
import unittest


def net_units(events: list[tuple[str, int]]) -> int:
    counts: Counter[str] = Counter()
    for sku, delta in events:
        counts[sku] += delta
    return counts.total()


class CounterTotalTests(unittest.TestCase):
    def test_includes_negative_adjustments(self) -> None:
        events = [("tea", 12), ("tea", -2), ("coffee", 5)]
        self.assertEqual(net_units(events), 15)

    def test_empty_counter_is_zero(self) -> None:
        self.assertEqual(net_units([]), 0)

    def test_zero_and_negative_keys_still_contribute(self) -> None:
        counts = Counter({"ready": 3, "held": -1, "empty": 0})
        self.assertEqual(counts.total(), 2)


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

HOW: total ไม่ได้บอกว่า inventory ถูกต้อง

ยอดรวมเป็นศูนย์อาจเกิดจากทุกอย่างหมดพอดี หรือมีสินค้า 100 ชิ้นหักลบกับ debt 100 ชิ้น การตัดสิน reorder จึงต้องดูราย key ไม่ใช่ total อย่างเดียว ถ้าค่าติดลบผิดกฎธุรกิจ ให้ validate หลังอัปเดตแต่ละ event และหยุด transaction ก่อน commit

Unary plus เช่น +counts สร้าง Counter ใหม่ที่เก็บเฉพาะ count บวก เหมาะกับรายการคงเหลือที่พร้อมใช้ แต่จะเปลี่ยนความหมายของ total() เพราะรายการติดลบและศูนย์ถูกตัดออก ต้องตั้งชื่อขั้น normalize ให้ชัดและอย่าใช้ก่อน audit adjustment

Recap

  • ใช้ total() เมื่ออยากได้ผลรวมสุทธิของ count ทั้งหมด
  • จำว่าศูนย์และค่าติดลบยังอยู่ใน Counter
  • ดูราย key สำหรับ invariant และ reorder decision
  • เก็บ event/audit trail ก่อน normalize ด้วย unary plus
  • ทดสอบ empty, negative adjustment และยอดหักล้างกัน