code: def cachedmethod(cache, key=keys.hashkey, lock=None):
      
      def decorator(method):
          if lock is None:
              def wrapper(self, *args, **kwargs):
                  c = cache(self)
                  if c is None:
                      return method(self, *args, **kwargs)
                  k = key(*args, **kwargs)
                  try:
                      return c[k]
                  except KeyError:
                      pass  # key not found
                  v = method(self, *args, **kwargs)
                  try:
                      c[k] = v
                  except ValueError:
                      pass  # value too large
                  return v
          else:
              def wrapper(self, *args, **kwargs):
                  c = cache(self)
                  if c is None:
                      return method(self, *args, **kwargs)
                  k = key(*args, **kwargs)
                  try:
                      with lock(self):
                          return c[k]
                  except KeyError:
                      pass  # key not found
                  v = method(self, *args, **kwargs)
                  try:
                      with lock(self):
                          c[k] = v
                  except ValueError:
                      pass  # value too large
                  return v
          return _update_wrapper(wrapper, method)
      return decorator
docs: Decorator to wrap a class or instance method with a memoizing
      callable that saves results in a cache.