Reputation: 77454
In a recent Hacker Newsletter issue, this very useful article about decorators in Python was linked. I like the article and I think I understand most of the decorator examples. However, in the non-decorator memoization example, I'm very confused by the code:
def memoize(fn):
stored_results = {}
def memoized(*args):
try:
# try to get the cached result
return stored_results[args]
except KeyError:
# nothing was cached for those args. let's fix that.
result = stored_results[args] = fn(*args)
return result
return memoized
I'm confused about how this function would create a persisting dictionary stored_results
that gets appended to. After re-reading it, copy/pasting it into my editor and playing with it, and looking online for help, I still don't understand what the syntax stored_results[args] = fn(*args)
is really doing.
(1) The article suggests that the above code will return the function, but that now it will search a dictionary first before executing on novel arguments. How does this happen? Why isn't stored_results
just local to memoize
? Why doesn't it get destroyed when memoized
is returned?
(2) Links to other questions or web resources that explain the argument passing here with *args
would be helpful too. If *args
is a list of arguments, why can we use the syntax stored_results[args]
, when normally you get a non-hashable error when trying to index a dictionary on a list?
Thanks for any clarifying thoughts.
Upvotes: 3
Views: 383
Reputation: 95318
If
*args
is a list of arguments, why can we use the syntaxstored_results[args]
, when normally you get a non-hashable error when trying to index a dictionary on a list?
Because it's not a list, it's a tuple. Lists are mutable, so you can't define a meaningful hash function on them. Tuples however are immutable data structures.
Why isn't stored_results just local to
memoize
? Why doesn't it get destroyed whenmemoized
is returned?
Because memoize
and memoized
share the same name context (closure). The closure persists because memoized
holds a reference to it and you return it and assign it to a global name (this is the effect of the decorator statement). With the closure, all the captured values persist as well.
Upvotes: 5