
Python lru_cache: จำผลคำนวณซ้ำให้ฟังก์ชันบริสุทธิ์เร็วขึ้น
เคยเรียกฟังก์ชันเดิมด้วยอาร์กิวเมนต์เดิมวนไปวนมาจนช้าไหม ผมเคย จนได้ลอง functools.lru_cache
ตามเอกสารมาตรฐาน lru_cache คือตัวตกแต่งที่เก็บผลลัพธ์ล่าสุดแบบ LRU (Least Recently Used — เตะรายการที่ไม่ได้ใช้มานานที่สุดเมื่อแคชเต็ม) เหมาะกับฟังก์ชันที่ผลขึ้นกับอินพุตอย่างเดียวและไม่มีผลข้างเคียง
ใช้เมื่อไหร่
เหมาะกับคำนวณซ้ำ ๆ เช่นแปลงพิกัด แตกพาร์สเล็ก ๆ หรือฟังก์ชันช่วยที่ถูกเรียกบ่อยในลูป ไม่เหมาะกับฟังก์ชันที่อ่านเวลาปัจจุบัน สุ่มค่า หรือเขียนไฟล์ เพราะแคชจะเก็บคำตอบเก่าไว้
อาร์กิวเมนต์ต้องแฮชได้ เช่น ตัวเลข สตริง ทูเปิลของค่าที่แฮชได้ อย่าส่งลิสต์หรือดิกต์ตรง ๆ
import unittest
from functools import lru_cache
@lru_cache(maxsize=32)
def slow_square(n: int) -> int:
slow_square.calls += 1
return n * n
slow_square.calls = 0
class LruCacheTests(unittest.TestCase):
def setUp(self):
slow_square.cache_clear()
slow_square.calls = 0
def test_repeated_args_hit_cache(self):
self.assertEqual(slow_square(3), 9)
self.assertEqual(slow_square(3), 9)
self.assertEqual(slow_square.calls, 1)
def test_different_args_compute_again(self):
slow_square(2)
slow_square(4)
self.assertEqual(slow_square.calls, 2)
self.assertEqual(slow_square.cache_info().hits, 0)
if __name__ == "__main__":
unittest.main()
cache_info() ช่วยดู hits/misses และ cache_clear() ล้างเมื่อข้อมูลต้นทางเปลี่ยน
จุดพลาดที่พบบ่อย
อย่าแคชฟังก์ชันที่ขึ้นกับสถานะภายนอกโดยไม่รู้ตัว จำกัด maxsize ให้เหมาะกับหน่วยความจำ และระวังการแคชค่าใหญ่เกินจำเป็น ถ้าต้องการแคชไม่จำกัดขนาด เอกสารมี cache = lru_cache(maxsize=None) แต่ต้องรับผิดชอบหน่วยความจำเอง
Recap
- ใช้
lru_cacheกับฟังก์ชันบริสุทธิ์ที่ถูกเรียกซ้ำ - ตรวจว่าอาร์กิวเมนต์แฮชได้
- ดู
cache_infoในเทสต์หรือดีบัก - ล้างแคชเมื่ออินพุตโลกภายนอกเปลี่ยน