code: def cache_decorator(function):
      
  
      # use wraps to preseve docstring
      @wraps(function)
      def get_cached(*args, **kwargs):
          """
          Only execute the function if its value isn't stored
          in cache already.
          """
          self = args[0]
          # use function name as key in cache
          name = function.__name__
          # do the dump logic ourselves to avoid
          # verifying cache twice per call
          self._cache.verify()
          # access cache dict to avoid automatic verification
          if name in self._cache.cache:
              # already stored so return value
              return self._cache.cache[name]
  
          # time execution
          tic = time.time()
          # value not in cache so execute the function
          value = function(*args, **kwargs)
          # store the value
          self._cache.cache[name] = value
          # debug log execution time
          # this is nice for debugging as you can see when
          # cache is getting dumped all the time
          log.debug('%s was not in cache, executed in %.6f',
                    name,
                    time.time() - tic)
          return value
  
      # all cached values are also properties
      # so they can be accessed like value attributes
      # rather than functions
      return property(get_cached)
docs: A decorator for class methods, replaces @property
      but will store and retrieve function return values
      in object cache.
  
      Parameters
      ------------
      function : method
        This is used as a decorator:
        ```
        @cache_decorator
        def foo(self, things):
          return 'happy days'
        ```