pandas-dev/pandas · error · TypeError

Expression objects are not iterable

Error message

Expression objects are not iterable

What it means

Raised by Expression.__iter__ (pandas/core/col.py:360). An Expression is a deferred, unevaluated reference to a column; it has no elements to iterate over until it is evaluated against a concrete DataFrame. Iteration (`for x in expr`, `list(expr)`, `*expr`) is therefore unsupported.

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 71959b8cb9)

Solutions

  1. Evaluate against a DataFrame first and iterate the resulting Series: `for v in df['x']:`.
  2. If you need the underlying values, call the expression's bound form or use `df['x'].tolist()`.
  3. Restrict pd.col usage to assign/loc/query/pipe which evaluate the deferred callable.

Example fix

# before
expr = pd.col('name')
for name in expr:
    ...

# after
for name in df['name']:
    ...
Defensive patterns

Strategy: type-guard

Validate before calling

from pandas.core.col import Expression

def to_iterable(obj, df):
    if isinstance(obj, Expression):
        return obj._eval_expression(df)
    return obj

Type guard

from pandas.core.col import Expression

def is_expression(obj) -> bool:
    return isinstance(obj, Expression)

Try / catch

try:
    values = list(expr)
except TypeError as e:
    if 'not iterable' in str(e):
        values = df[expr._eval_expression(df).name].tolist()
    else:
        raise

Prevention

When it happens

Trigger: `for v in pd.col('x'):`, `list(pd.col('x'))`, `[*pd.col('x')]`, or unpacking `a, b = pd.col('x')`. Passing an Expression to a function that iterates its argument (sum, map, list, tuple, set).

Common situations: Assuming `pd.col('x')` returns the column's values directly. Mixing deferred Expression objects into generic collection-handling helpers that iterate inputs.

Related errors


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