Well, I think you could still improve upon it greatly with a 5 line except clause:

```python
try:
    cache[key] = fn(*args, **kwargs)
except TypeError as e:
    try:
        [hash(elem) for elem in itertools.chain(args, kwargs.itervalues())]
    except TypeError:
        raise TypeError("argument %s unhashable in memoized function %s" % (str(elem), str(fn))
    raise

It's less of a pain to debug (you might have several arguments that could've potentially turned into list, and the standard exception only gives you the type, not the value, and knowing that this happened inside a memoization _and of which function_ is a great help) and doesn't impact runtime much since any extra work is only done in the except clause.

I can't understate the value of knowing which memoized function this is taking place inside. It's one of the more annoying things about using decorators, IMHO.