pandas-dev/pandas · error · TypeError

Expression objects are not copiable

Error message

Expression objects are not copiable

What it means

Raised by Expression.__copy__ (typed NoReturn) when copy.copy() is called on a pandas.api.typing.Expression. Expression objects intentionally forbid shallow copies to preserve their identity as deferred singletons bound to a specific closure; copying them could break the func->DataFrame evaluation chain. The same message is reused for both __copy__ and __deepcopy__.

Solutions

  1. Do not copy Expression objects; treat them as opaque singletons.
  2. Filter Expression out before copying: skip objects whose type name is 'Expression'.
  3. Re-create the Expression from its source via pd.col(name) if you need a logically equivalent one.

Example fix

// before
expr2 = copy.copy(pd.col('a'))
// after
expr2 = pd.col('a')  # same singleton, or rebuild from name
Defensive patterns

Strategy: type-guard

Validate before calling

from pandas.api.typing import Expression
if isinstance(obj, Expression):
    raise TypeError('do not copy.copy() an Expression')

Type guard

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

Prevention

When it happens

Trigger: copy.copy(pd.col('a')); a library that auto-copies its inputs (e.g. functools.partial dispatch, some ORM wrappers); pickle/copy protocols that fall back to __copy__.

Common situations: Calling copy.copy on a heterogeneous collection that happens to contain an Expression; framework code that defensively copies user-supplied callables.

Related errors


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

Appendix: source

Thrown at pandas/core/col.py:363

                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

    Parameters
    ----------

View on GitHub (pinned to 3b7651241d)