docs: def value_comparisons(self, values, comp="=", is_assignment=False):
      """Builds out a series of value comparisions.
  
      :values: can either be a dictionary, in which case the return will compare a name to a named
      placeholder, using the comp argument. I.E. values = {"first_name": "John", "last_name": "Smith"}
      will return ["first_name = %(first_name)s", "last_name = %(last_name)s"].
  
      Otherwise values will be assumed to be an iterable of 2- or 3-tuples in the form
      (column, value[, operator]). When operator is not specified, it will fallback to comp. So for
      instance values = [("first_name", "John"), ("id", (10, 100), "between")] will return
      ["first_name = %s", "id between %s and %s "].
  
      :is_assigment: if False, transform_op will be called on each operator.
      """
      if isinstance(values, dict):
        if self.sort_columns:
          keys = sorted(values.keys())
        else:
          keys = list(values.keys())
  
        params = zip(keys, [self.to_placeholder(k) for k in keys])
        return [
          self.to_expression(
            i[0],
            i[1],
            comp if is_assignment else self.transform_op(comp, values[i[0]]))
          for i in params]
      else:
        if self.sort_columns:
          values = sorted(values, key=operator.itemgetter(0))
        comps = []
        for val in values:
          lhs = val[0]
          op = val[2] if len(val) == 3 else comp
          if op == "raw":
            rhs = None
          elif op == "between":
            rhs = (self.to_placeholder(), self.to_placeholder())
          elif op == "in":
            rhs = [self.to_placeholder() for i in val[1]]
          else:
            rhs = self.to_placeholder()
            if not is_assignment:
              op = self.transform_op(op, val[1])
  
          comps.append(self.to_expression(lhs, rhs, op))
        return comps