code: def memoize(fn):
      
      cache = {}
  
      @wraps(fn)
      def wrapped_fn(*args, **kwargs):
          cache_key = _memoize_cache_key(args, kwargs)
          try:
              return cache[cache_key]
          except KeyError:
              value = fn(*args, **kwargs)
              cache[cache_key] = value
              return value
  
      def clear_cache():
          cache.clear()
  
      # Needed to ensure that EnsemblRelease.clear_cache
      # is able to clear memoized values from each of its methods
      wrapped_fn.clear_cache = clear_cache
      # expose the cache so we can check if an item has already been computed
      wrapped_fn.cache = cache
      # if we want to check whether an item is in the cache, first need
      # to construct the same cache key as used by wrapped_fn
      wrapped_fn.make_cache_key = _memoize_cache_key
      return wrapped_fn
docs: Simple reset-able memoization decorator for functions and methods,
      assumes that all arguments to the function can be hashed and
      compared.