import functools


def memoize(f):
  """Decorator that can be applied to a method to memoize the results.

  Args:
    f - The function to decorate with memoization. The first argument of
      the function should be self - the instance of the class. The
      function can take an arbitrary amount of additional positional
      arguments. All arguments must be hashable.

  Returns:
    A function wrapping `f`. `f` will be executed only once for a given
    set of input arguments.
  """

  cache = {}

  @functools.wraps(f)
  def cached(self, *args):
    if args in cache:
      return cache[args]
    ret = f(self, *args)
    cache[args] = ret
    return ret

  return cached