pola-rs/polars · error · TypeError

unsupported operand type(s) for op: ('Expr' - 'Selector')

Error message

unsupported operand type(s) for op: ('Expr' - 'Selector')

What it means

Selector.__rsub__ always raises: `something - selector` where the left operand is not itself a selector (a scalar, numpy array, etc.) is rejected instead of being guessed as either set-difference or arithmetic subtraction with reflected operands.

Source

Thrown at py-polars/src/polars/selectors.py:524

        return self.as_expr().__ror__(other)

    @overload
    def __sub__(self, other: Selector) -> Selector: ...

    @overload
    def __sub__(self, other: Any) -> Expr: ...

    def __sub__(self, other: Any) -> Selector | Expr:
        if is_selector(other):
            return Selector._from_pyselector(
                PySelector.difference(self._pyselector, other._pyselector)
            )
        else:
            return self.as_expr().__sub__(other)

    def __rsub__(self, other: Any) -> NoReturn:
        msg = "unsupported operand type(s) for op: ('Expr' - 'Selector')"
        raise TypeError(msg)

    @overload
    def __xor__(self, other: Selector) -> Selector: ...

    @overload
    def __xor__(self, other: Any) -> Expr: ...

    def __xor__(self, other: Any) -> Selector | Expr:
        if is_column(other):  # @2.0: remove
            other = by_name(other.meta.output_name())
        if is_selector(other):
            return Selector._from_pyselector(
                PySelector.exclusive_or(self._pyselector, other._pyselector)
            )
        else:
            return self.as_expr().__xor__(other)

    def __rxor__(self, other: Any) -> Expr:

View on GitHub (pinned to df599052da)

Solutions

  1. Move the selector to the left for ARITHMETIC subtraction: cs.numeric() - 1 (Selector.__sub__ with a non-selector does expression arithmetic)
  2. For constant-minus-column, convert the selector first: pl.lit(1) - cs.numeric().as_expr()
  3. For set difference keep the selector left: cs.all() - cs.by_name('id')
  4. Select the columns first, then operate on them in a subsequent step

Example fix

# before
df.select(1 - cs.numeric())

# after
df.select(pl.lit(1) - cs.numeric().as_expr())
# arithmetic with selector on the left also works:
df.select((cs.numeric() - 1).name.suffix('_dec'))
Defensive patterns

Strategy: type-guard

Validate before calling

from polars.selectors import is_selector
if is_selector(right) and not is_selector(left):
    expr = pl.lit(left) - right.as_expr()  # never rely on `left - selector`

Type guard

from polars.selectors import is_selector

def safe_sub(left, right):
    if is_selector(right):
        return right.as_expr().__rsub__(left)
    return left - right

Prevention

When it happens

Trigger: 1 - cs.numeric() in select/with_columns; np_array - cs.all(); any expression where a Selector ends up on the right side of a minus with a non-selector left operand.

Common situations: Writing constant-minus-column expressions (deltas, inversions) with the selector on the right; vectorized numpy-style code pasted into polars expressions.

Related errors


AI-assisted analysis of pola-rs/polars@df599052da (2026-08-16). Data as JSON: /api/errors/eaa91dffd7062e31. Report an issue: GitHub.