Skip to content

← Everyday Standard Library Tools step 27 of 55

Medium Primitives

Give Each Object Its Own Cache

The starter's Digest caches value with @lru_cache on the method. That one cache serves every instance and holds each one in its keys, so a Digest stays in memory after you have finished with it. Change Digest so that each instance keeps its own results and Python can free it when nothing else refers to it.

d = Digest(-4)
d.value(4)  # 0
d.value(4)  # 0, read from the cache
d.calls     # 1

What to write

Fill in Digest(salt):

  • value(n) returns (self.salt + n) ** 2.
  • calls starts at 0. Add 1 only when value computes a result it has not stored yet. Store results of 0 too.
  • Each instance has its own cache and its own calls, even when two instances have the same salt.
  • Keep every result for the life of the instance. A cache with a size limit could throw a result out and compute it again, which would make calls too high.
  • The salt never changes after __init__, so a stored result never goes stale.
  • Keep Digest a normal class. The report makes a weak reference to it, and a class with __slots__ allows that only if its slots include __weakref__.

What the report shows

Keep solve as it is. It creates a Digest with salt and calls value on every query, twice over. Then it creates a second Digest with other_salt and calls value on every query once. Last, it makes a weak reference to the first Digest, deletes its own reference, and runs gc.collect(). A weak reference lets you check whether an object still exists without keeping it alive.

Key Meaning
pass_one, pass_two The first instance's results for each pass, in query order, duplicates included
computations The first instance's calls after both passes
other The second instance's results
other_computations The second instance's calls
leaked True if the first instance still exists after gc.collect()

The tests expect both counts to equal the number of distinct queries, and leaked to be False. The starter already gets the results and counts right. Only leaked is wrong. Once there is at least one query, the shared cache still holds the first instance.

For salt=-4, other_salt=4 and queries=[4, 4], both passes are [0, 0], other is [64, 64], and each instance computes once.

Edge cases

  • Empty queries give empty lists, two counts of 0, and leaked set to False.