I've been using the following memoizing decorator (from the great book Python Algorithms: Mastering Basic Algorithms in the Python Language ... love it, btw).
def memo(func):
cache = {}
@ wraps(func)
def wrap(*args):
if args not in cache:
cache[args] = func(*args)
return cache[args]
return wrap
The problem with this decorator is that the dictionary-based cache means that all of my arguments must be hashable.
Does anyone have an implementation (or a tweak to this one) that allows for unhashable arguments (e.g. dictionaries)?
I know that the lack of a hash value means that the question of "is this in the cache?" becomes non-trivial, but I just thought I'd ask.
===EDITED TO GIVE CONTEXT===
I am working on a function that returns a Parnas-style "uses hierarchy" given a dictionary of module: dependencies. Here's the setup:
def uses_hierarchy(requirements):
"""
uses_hierarchy(requirements)
Arguments:
requirements - a dictionary of the form {mod: list of dependencies, }
Return value:
A dictionary of the form {level: list of mods, ...}
Assumptions:
- No cyclical requirements (e.g. if a requires b, b cannot require a).
- Any dependency not listed as a mod assumed to be level 0.
"""
levels = dict([(mod, _level(mod, requirements))
for mod in requirements.iterkeys()])
reversed = dict([(value, []) for value in levels.itervalues()])
for k, v in levels.iteritems():
reversed[v].append(k)
return reversed
def _level(mod, requirements):
if not requirements.has_key(mod):
return 0
dependencies = requirements[mod]
if not dependencies:
return 0
else:
return max([_level(dependency, requirements)
for dependency in dependencies]) + 1
So that:
>>> requirements = {'a': [],
... 'b': [],
... 'c': ['a'],
... 'd': ['a','b'],
... 'e': ['c','d'],
... 'f': ['e']
... }
>>> uses_hierarchy(requirements)
{0: ['a', 'b'], 1: ['c', 'd'], 2: ['e'], 3: ['f']}
_level is the function I want to memoize to make this setup more scalable. As implemented without memoization, it calculates the level of dependencies multiple times (e.g. 'a' is calculated 8 times I think in the example above).
Thanks,
Mike
Technically you can solve this problem by turning the
dict
(orlist
orset
) into a tuple. For example:But I wouldn't do this in
memo
, I'd rather change the functions you want to use memo on - for example, instead of accepting adict
they should only accept(key, value)
pairs, instead of taking lists or sets they should just take*args
.Since no one else has mentioned it, the Python Wiki has a Decorator Library which includes a number of memoizing decorator patterns.
My personal preference is the last one, which lets calling code simply treat the method as a lazily-evaluated property, rather than a method. But I like the implementation here better.
In the same file linked above there's also a
lazy_dict
decorator, which lets you treat a function as a dictionary with lazily-evaluated values.Here is the example in Alex Martelli Python Cookbook that show how to create a memoize decorator using cPickle for function that take mutable argument (original version) :
Here is a link.
EDIT: Using the code that you have given and this memoize decorator :
i was able to reproduce this:
Not heavily tested, but seems to work: