def cache(func):
    """ A thin middleware that caches based on function name and arguments. """
        def _inner(*args, **kwargs):
            a_str = "&".join([str(a) or None for a in args])
            kw_str = "&".join(["%s=%s" for key, val in kwargs.iteritems()])
            key_name = slugify("%s:%s:%s" % (func.__name__, a_str, kw_str))
            cached = cache.get(key_name)
            if not cached:
                cached = func(*args, **kwargs)
                cache.set(key_name, cached)
            return cached
        return _inner

This is designed with django in mind, but you can replace cache.set and cache.get with any cache cache framework of your choice. Also, I use slugify because many cache frameworks don’t like spaces in cache key names. Slugify replaces them with dashes.

Usage is straightforward

@cache
def expensive_method(a,b=True):
    ...