Helper for :meth:`delete_cell` and :meth:`delete_cells`.

        ``state`` is unused by :class:`DirectRow` but is used by
        subclasses.

        :type column_family_id: str
        :param column_family_id: The column family that contains the column
                                 or columns with cells being deleted. Must be
                                 of the form ``[_a-zA-Z0-9][-_.a-zA-Z0-9]*``.

        :type columns: :class:`list` of :class:`str` /
                       :func:`unicode <unicode>`, or :class:`object`
        :param columns: The columns within the column family that will have
                        cells deleted. If :attr:`ALL_COLUMNS` is used then
                        the entire column family will be deleted from the row.

        :type time_range: :class:`TimestampRange`
        :param time_range: (Optional) The range of time within which cells
                           should be deleted.

        :type state: bool
        :param state: (Optional) The state that is passed along to
                      :meth:`_get_mutations`.

def _delete_cells(self, column_family_id, columns, time_range=None, state=None):
        """Helper for :meth:`delete_cell` and :meth:`delete_cells`.

        ``state`` is unused by :class:`DirectRow` but is used by
        subclasses.

        :type column_family_id: str
        :param column_family_id: The column family that contains the column
                                 or columns with cells being deleted. Must be
                                 of the form ``[_a-zA-Z0-9][-_.a-zA-Z0-9]*``.

        :type columns: :class:`list` of :class:`str` /
                       :func:`unicode <unicode>`, or :class:`object`
        :param columns: The columns within the column family that will have
                        cells deleted. If :attr:`ALL_COLUMNS` is used then
                        the entire column family will be deleted from the row.

        :type time_range: :class:`TimestampRange`
        :param time_range: (Optional) The range of time within which cells
                           should be deleted.

        :type state: bool
        :param state: (Optional) The state that is passed along to
                      :meth:`_get_mutations`.
        """
        mutations_list = self._get_mutations(state)
        if columns is self.ALL_COLUMNS:
            mutation_val = data_v2_pb2.Mutation.DeleteFromFamily(
                family_name=column_family_id
            )
            mutation_pb = data_v2_pb2.Mutation(delete_from_family=mutation_val)
            mutations_list.append(mutation_pb)
        else:
            delete_kwargs = {}
            if time_range is not None:
                delete_kwargs["time_range"] = time_range.to_pb()

            to_append = []
            for column in columns:
                column = _to_bytes(column)
                # time_range will never change if present, but the rest of
                # delete_kwargs will
                delete_kwargs.update(
                    family_name=column_family_id, column_qualifier=column
                )
                mutation_val = data_v2_pb2.Mutation.DeleteFromColumn(**delete_kwargs)
                mutation_pb = data_v2_pb2.Mutation(delete_from_column=mutation_val)
                to_append.append(mutation_pb)

            # We don't add the mutations until all columns have been
            # processed without error.
            mutations_list.extend(to_append)