Python always computes all arguments you're passing to a function, and only then does it call the function. In other words, like most other languages, Python is "eager" in its evaluation (the major exception today is probably Haskell, but that doesn't help you;-).
So setdefault
is a very unsuitable approach for caching! Whenever you do
cache.setdefault(akey, f(x, y))
you're first calling f(x, y)
, with all its computational cost, then maybe dropping the results of that computation on the floor; this makes the caching totally ineffective.
Rather, always do it as follows:
akey = whatever(x, y)
if akey not in cache:
cache[akey] = f(x, y)
return cache[akey]
or the like -- there are a few other possible idioms, especially if e.g. you know that f
will never return None
:
result = cache.get(akey)
if result is None:
result = cache[akey] = f(x, y)
return result
As for the secondary issue of what's an appropriate whatever
for the key computation, given that you know that f
is symmetrical, I think frozenset
is probably OK; although (if the components of x
and y
are comparable, as well as hashable -- i.e. it wouldn't work with complex numbers) you might want to consider
ta1 = tuple(a1)
ta2 = tuple(a2)
if ta1 > ta2: key = ta1, ta2
else: key = ta2, ta1
the relative performance depends on the cost of comparing, vs that of hashing, the items in a1
and a2
. Differences are likely to be minor, anyway.