'Produce a memoizing version of f.
Requires that all parameters are hashable. Also, it does not copy the return
value, so changes to a returned object may be visible in future invocations.
Args:
f: The function which will be wrapped.
Returns:
A wrapped function which memoizes all values returned by f, keyed by
the arguments to f.'

def __call__(self, f):

    f.memo_pad = {}
    f.memo_deep_copy = self.deep_copy
    @functools.wraps(f)
    def Wrapped(*args, **kwargs):
        key = (tuple(args), tuple(sorted(kwargs.items(), key=(lambda x: x[0]))))
        if (key not in f.memo_pad):
            f.memo_pad[key] = f(*args, **kwargs)
        if f.memo_deep_copy:
            return copy.deepcopy(f.memo_pad[key])
        else:
            return f.memo_pad[key]
    return Wrapped
