Een lru_cache met maximaal twee bewaarde aanroepen gebruiken.
Hits, misses en verwijderen van oude resultaten waarnemen.
Een bewaarde lijst beschermen en een cache bewust leegmaken.
Een functie rekent steeds hetzelfde resultaat voor dezelfde artikelcode uit. Een cache kan eerder werk opnieuw gebruiken. Dat bespaart soms berekeningen, maar bewaart ook gegevens die anders misschien al uit het geheugen verdwenen waren.
Een antwoord opnieuw gebruiken
Een cache bewaart een resultaat bij een sleutel. Bij lru_cache vormen de functieargumenten die sleutel. De argumenten moeten daarom hashbaar zijn; een lijst kun je niet rechtstreeks als cacheargument gebruiken.
Een hit betekent dat het antwoord al beschikbaar was. Een miss betekent dat de oorspronkelijke functie moest worden uitgevoerd. De cache-info houdt beide aantallen bij, naast de ingestelde bovengrens en het huidige aantal bewaarde aanroepen.
LRU staat voor least recently used: bij een volle cache verdwijnt de aanroep die het langst niet gebruikt is. Een hit telt daarbij als nieuw gebruik. Met maxsize=2 kunnen maximaal twee sleutel-resultaatcombinaties bewaard blijven.
De cache-info volgt echte bronaanroepen
from functools import lru_cache
@lru_cache(maxsize=2)
def square(value):
print(f"reken {value}")
return value * value
print(square(3))
print(square(3))
info = square.cache_info()
print(info.hits, info.misses, info.currsize)
Verwachte uitvoer
reken 3
9
9
1 1 1
De bronfunctie print alleen wanneer zij echt rekent. De tweede aanroep voor dezelfde waarde gebruikt het opgeslagen antwoord en print dus geen nieuwe rekenregel. De teller laat hetzelfde onderscheid zien zonder een toevallige tijdmeting.
Een grens in aantallen is geen grens in bytes
Een bewaard resultaat kan klein of groot zijn. Twee volledige rapporten kosten meestal meer geheugen dan twee integers. maxsize=2 begrenst dus het aantal entries, niet het aantal bytes van alle bereikbare objecten.
De cache houdt verwijzingen vast naar argumenten en retourwaarden. Die blijven bereikbaar totdat de entry verdwijnt of de cache wordt leeggemaakt. Een onbeperkte cache kan bij steeds nieuwe sleutels blijven groeien; kies dat niet automatisch voor invoer van bezoekers.
Een gedeeld object blijft hetzelfde object
Een cache kopieert zijn resultaat niet. Als de functie een lijst teruggeeft, kan een aanroeper de bewaarde lijst veranderen. Een volgende hit ontvangt dan diezelfde gewijzigde lijst.
Een hit geeft hetzelfde opgeslagen object
from functools import lru_cache
@lru_cache(maxsize=2)
def labels(code):
return [code]
first = labels("A")
first.append("gewijzigd")
second = labels("A")
print(second)
print(first is second)
copy = list(second)
copy.append("alleen kopie")
print(second)
Verwachte uitvoer
['A', 'gewijzigd']
True
['A', 'gewijzigd']
Dat gedrag is geen fout in lru_cache: de lijst is echt dezelfde. Als je callers alleen een eigen buitenste lijst wilt geven, maak je bij het ophalen een ondiepe kopie. Geneste objecten worden daarmee nog niet automatisch onafhankelijk.
Veelgemaakte fout: oude antwoorden blijven geldig lijken
Een functie die de huidige voorraad leest, is niet vanzelf geschikt om onbeperkt te cachen. Haar argument kan hetzelfde blijven terwijl de voorraad verandert. Een hit levert dan een oud antwoord zonder dat de bron opnieuw wordt geraadpleegd.
Gebruik caching daarom voor een duidelijk contract over geldigheid. Je kunt een cache bijvoorbeeld legen nadat de achterliggende gegevens veranderd zijn. cache_clear verwijdert entries en zet de hit- en misstellers terug naar nul.
Een cache is bovendien geen garantie dat een functie bij gelijktijdige aanvragen maar eenmaal wordt uitgevoerd. Deze oefening gebruikt uitsluitend opeenvolgende aanroepen. Synchronisatie en gelijktijdige uitvoering zijn een afzonderlijk onderwerp.
Bewaren, kopiëren en vrijgeven
Je bouwt eerst een echte cache van hoogstens twee entries. Daarna schrijf je een ophaalfunctie voor lijstresultaten, een expliciete reset en een uitleesfunctie voor de vier statistieken. De meegeleverde helpers gebruiken echte lru_cache-objecten en echte bronaanroepen.
De resetcontrole bewaart daarnaast alleen een zwakke verwijzing naar een resultaat. Zo kan zij zien dat het object vóór de reset door de cache bereikbaar blijft en na het wissen niet meer. Dit is geen meting van alle gebruikte bytes, maar wel een concrete controle op vasthouden en vrijgeven.
AAN DE SLAG 0/4
1
Een cache met twee entries
Vul make_cached(fn) in: geef een echte functools.lru_cache(maxsize=2)-wrapper om fn. Behoud normale retourwaarden en __wrapped__; herhaalde sleutels gebruiken een hit en volle caches verwijderen de minst recent gebruikte entry.
2
Een eigen buitenste lijst ophalen
Vul lookup_copy(cached, key) in: roep cached(key) eenmaal aan en geef een nieuwe ondiepe lijstkopie terug. Wijzig de opgeslagen lijst niet en laat eerdere kopieën onafhankelijk blijven.
3
Resultaten en tellers vrijgeven
Vul clear_cache(cached) in: wis de echte cache met cache_clear, zodat entries en hit/misstellers verdwijnen. Geef None terug; een volgende aanroep moet de bron opnieuw uitvoeren.
4
Statistieken met hun betekenis uitlezen
Vul cache_stats(cached) in: geef een tuple (hits, misses, maxsize, currsize) uit de actuele cache_info terug. Vraag daarvoor geen nieuw bronresultaat op.
Samenvatting
Een cache bewaart werk bij argumenten; hits en misses vertellen wat echt gebeurde.
Een entrygrens begrenst geen bytes en voorkomt geen verouderde inhoud.
Kopieer veranderlijke lijstresultaten gericht en maak een cache bewust leeg.
Hierna kies en onderbouw je een verbetering in een kleine prijsrapportage.