Memoizing decorator for f, which must have exactly 1 hashable argument.

def _memoize(f):
  """Memoizing decorator for f, which must have exactly 1 hashable argument."""
  nothing = object()  # Unique "no value" sentinel object.
  cache = {}
  # Use a reentrant lock so that if f references the resulting wrapper we die
  # with recursion depth exceeded instead of deadlocking.
  lock = threading.RLock()
  @functools.wraps(f)
  def wrapper(arg):
    if cache.get(arg, nothing) is nothing:
      with lock:
        if cache.get(arg, nothing) is nothing:
          cache[arg] = f(arg)
    return cache[arg]
  return wrapper