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
- Evaluate against a DataFrame first: series = pd.col('a')._eval_expression(df), then iterate series.
- Use df['a'] directly when you already have the DataFrame in scope.
- 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
- Differentiate pd.col('x') (deferred) from df['x'] (materialized) before iteration.
- Type-check inputs in generic iteration helpers.
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
- boolean value of an expression is ambiguous
- Expression objects are not copiable
- Column ' ' not found in given DataFrame. Hint: did you mean…
- Expected Hashable, got
- Accumulation not supported for
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.0View on GitHub (pinned to 3b7651241d)