pandas-dev/pandas · error · TypeError

Expression objects are not iterable

Error message

Expression objects are not iterable

What it means

Raised by Expression.__iter__ (typed NoReturn) when code tries to iterate a pandas.api.typing.Expression. An Expression is a deferred column descriptor, not a container of values, so iteration has no meaning until it is evaluated against a DataFrame. Any for loop, unpacking, list(), tuple(), or *expr spread triggers it.

Solutions

  1. Evaluate against a DataFrame first: series = pd.col('a')._eval_expression(df), then iterate series.
  2. Use df['a'] directly when you already have the DataFrame in scope.
  3. Type-check inputs in generic helpers: reject pandas.api.typing.Expression before iterating.

Example fix

// before
for v in pd.col('speed'):
    ...
// after
for v in df['speed']:
    ...
Defensive patterns

Strategy: type-guard

Validate before calling

from pandas.api.typing import Expression
if isinstance(x, Expression):
    raise TypeError('evaluate Expression against a DataFrame before iterating')

Type guard

def is_expression(x) -> bool:
    from pandas.api.typing import Expression
    return isinstance(x, Expression)

Prevention

When it happens

Trigger: for v in pd.col('a'): ...; list(pd.col('a')); a, b = pd.col('a'); zip(pd.col('a'), other); passing an Expression to a function that iterates its argument.

Common situations: Confusing pd.col('a') (deferred) with df['a'] (materialized); writing generic code that iterates any input and accidentally receiving an Expression.

Related errors


AI-assisted analysis of pandas-dev/pandas@3b7651241d (2026-08-11). Data as JSON: /api/errors/40671dcd981af9cd. Report an issue: GitHub.

Appendix: source

Thrown at pandas/core/col.py:360

                    condition = condition._eval_expression(df)
                if isinstance(replacement, Expression):
                    replacement = replacement._eval_expression(df)
                evaluated.append((condition, replacement))
            return ser.case_when(evaluated)

        # Keep repr compact; caselist may be large.
        repr_str = f"{self!r}.case_when(...)"
        return Expression(func, repr_str)

    def __repr__(self) -> str:
        return self._repr_str or "Expr(...)"

    # Unsupported ops
    def __bool__(self) -> NoReturn:
        raise TypeError("boolean value of an expression is ambiguous")

    def __iter__(self) -> NoReturn:
        raise TypeError("Expression objects are not iterable")

    def __copy__(self) -> NoReturn:
        raise TypeError("Expression objects are not copiable")

    def __deepcopy__(self, memo: dict[int, Any] | None) -> NoReturn:
        raise TypeError("Expression objects are not copiable")


@set_module("pandas")
def col(col_name: Hashable) -> Expression:
    """
    Generate deferred object representing a column of a DataFrame.

    Any place which accepts ``lambda df: df[col_name]``, such as
    :meth:`DataFrame.assign` or :meth:`DataFrame.loc`, can also accept
    ``pd.col(col_name)``.

    .. versionadded:: 3.0.0

View on GitHub (pinned to 3b7651241d)