2

I have a function foo(a, b, c= False, d = 0), and I need to create a cache dict inside the function for each combination of the parameters.

The examples I have seen online only use *args, but I need to get the kwargs-values too, in the correct order, and store them in a cache dict, and handle the case that dicts cannot be keys, and kwargs is a dict.

So I did it like this: is this the way to go?

foo(*args, **kwargs):
     if (*args, *kwargs.values()) in cache:
         return cache[(*args, *kwargs.values())]

Basically I convert the kwargs to values (in the order of insertion since I am using Python3.6, which will be what I want) and then combine with *args to create a tuple.

SUBZERO
  • 107
  • 1
  • 8
  • What's wrong with `functools.lru_cache`? – superb rain Jan 09 '21 at 12:59
  • 1
    @superbrain Is it faster than a manual solution? If its only for brevity's sake, then I'd rather code it myself and be explicit about what I'm doing, than rely on somebody else's code that I don't understand. – SUBZERO Jan 09 '21 at 13:02
  • @superbrain Also, there's a post on this site regarding some memory leaks from lru_cache, so another reason to do it myself. – SUBZERO Jan 09 '21 at 13:03
  • I don't know. Try it? :-) – superb rain Jan 09 '21 at 13:03
  • Note that [`lru_cache`](https://docs.python.org/3/library/functools.html#functools.lru_cache) doesn't canonicalise the arguments, so e.g. changing the order of the keyword arguments would mean a cache miss. – jonrsharpe Jan 09 '21 at 13:03
  • Why not share that memory leak post? – superb rain Jan 09 '21 at 13:04
  • @jonrsharpe Better than the OP's collisions, I'd say :-) – superb rain Jan 09 '21 at 13:06
  • If you just want to handle that *one specific function*, you might be better bringing the cache *inside* that function, then you know what's assigned to each parameter and can check e.g. `if (a, b, c, d) not in foo._cache:` – jonrsharpe Jan 09 '21 at 13:08
  • Right, but as long as *kwargs.values() gives me (c, d), and I get (a, b) from *args, then it should be the same. – SUBZERO Jan 09 '21 at 13:09
  • But that *won't* always be the case. In the case of `foo(b=1, d=2, a=3, c=4)`, for example, the keyword argument values would actually be `(b, d, a, c)` and the positional arguments empty. Depending on your Python version you could use positional-only args to limit the calling options (see e.g. https://stackoverflow.com/a/28181228/3001761 for other implications of that), but any keyword args could change order. – jonrsharpe Jan 09 '21 at 13:11
  • Oh ok, I guess I thought you couldn't switch positions of arguments like that, so I thought they'd always be in order. Ok so I should probably use lru_cache (since I don't know the arguments, it's a wrapper-type situation). – SUBZERO Jan 09 '21 at 13:16
  • You could define an inner function `_foo(a,b,c,d)` with a wrapper `foo(...)` accepting kwargs. – VPfB Jan 09 '21 at 14:08
  • Use frozenset. [python - Hashing a dictionary? - Stack Overflow](https://stackoverflow.com/questions/5884066/hashing-a-dictionary) – user202729 Jan 11 '21 at 04:28

0 Answers0