docs: def get(self, cls, id_field, id_val):
          """
          Retrieve an object which `id_field` matches `id_val`. If it exists in
          the cache, it will be fetched from Redis. If not, it will be fetched
          via the `fetch` method and cached in Redis (unless the cache flag got
          invalidated in the meantime).
          """
          cache_key, flag_key = self.get_keys(cls, id_field, id_val)
  
          result = self.get_cached_or_set_flag(keys=(cache_key, flag_key))
  
          # in Lua, arrays cannot hold nil values, so e.g. if [1, nil] is returned,
          # we'll only get [1] here. That's why we need to append None ourselves.
          if len(result) == 1:
              result.append(None)
  
          previous_flag, cached_data = result
  
          # if cached data was found, deserialize and return it
          if cached_data is not None:
              deserialized = self.deserialize(cls, cached_data)
  
              # verify that the cached object matches our expectations
              # if not, return from the persistant storage instead.
              if self.verify(cls, id_field, id_val, deserialized):
                  return deserialized
              else:
                  # invalidate the cache if it didn't pass verification
                  self.invalidate(cls, id_field, id_val)
  
          obj = self.fetch(cls, id_field, id_val)
  
          # If the flag wasn't previously set, then we set it and we're responsible
          # for putting the item in the cache. Do this unless the cache got
          # invalidated and the flag was removed.
          if not previous_flag:
              obj_serialized = self.serialize(obj)
              self.cache(keys=(cache_key, flag_key), args=(obj_serialized,))
  
          return obj