{"record":{"id":"67cb73c86e94a03d","repo":"pandas-dev/pandas","slug":"expression-objects-are-not-copiable","errorCode":null,"errorMessage":"Expression objects are not copiable","messagePattern":"Expression objects are not copiable","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"pandas/core/col.py","lineNumber":363,"sourceCode":"                evaluated.append((condition, replacement))\n            return ser.case_when(evaluated)\n\n        # Keep repr compact; caselist may be large.\n        repr_str = f\"{self!r}.case_when(...)\"\n        return Expression(func, repr_str)\n\n    def __repr__(self) -> str:\n        return self._repr_str or \"Expr(...)\"\n\n    # Unsupported ops\n    def __bool__(self) -> NoReturn:\n        raise TypeError(\"boolean value of an expression is ambiguous\")\n\n    def __iter__(self) -> NoReturn:\n        raise TypeError(\"Expression objects are not iterable\")\n\n    def __copy__(self) -> NoReturn:\n        raise TypeError(\"Expression objects are not copiable\")\n\n    def __deepcopy__(self, memo: dict[int, Any] | None) -> NoReturn:\n        raise TypeError(\"Expression objects are not copiable\")\n\n\n@set_module(\"pandas\")\ndef col(col_name: Hashable) -> Expression:\n    \"\"\"\n    Generate deferred object representing a column of a DataFrame.\n\n    Any place which accepts ``lambda df: df[col_name]``, such as\n    :meth:`DataFrame.assign` or :meth:`DataFrame.loc`, can also accept\n    ``pd.col(col_name)``.\n\n    .. versionadded:: 3.0.0\n\n    Parameters\n    ----------","sourceCodeStart":345,"sourceCodeEnd":381,"githubUrl":"https://github.com/pandas-dev/pandas/blob/71959b8cb9b2459c16e14b34f28b178ccfe14735/pandas/core/col.py#L345-L381","documentation":"Raised by Expression.__copy__ (pandas/core/col.py:363). Expression objects capture a closure over a column reference (col_name) and a repr string; copying them would duplicate a deferred callable with no meaningful independent state, so copy.copy() is intentionally blocked to avoid silent aliasing bugs.","triggerScenarios":"`copy.copy(pd.col('x'))`, or passing an Expression through a pipeline/library that defensively shallow-copies its inputs (e.g. some sklearn transformers, multiprocessing forks, deepcopy-heavy config loaders).","commonSituations":"Generic utility code that calls copy.copy on arbitrary objects. Deep-copy of a dict/list that happens to contain an Expression.","solutions":["Do not copy the Expression; reconstruct it via `pd.col(col_name)` if you need a fresh reference.","Remove the Expression from data structures that get copied, or replace it with the evaluated Series before copying.","If a library force-copies, evaluate the expression against the DataFrame first so you copy a concrete Series instead."],"exampleFix":"# before\nimport copy\nexpr = pd.col('x')\nexpr2 = copy.copy(expr)\n\n# after\nexpr = pd.col('x')\nexpr2 = pd.col('x')  # construct a fresh Expression","handlingStrategy":"type-guard","validationCode":"from pandas.core.col import Expression\n\ndef safe_copy(obj):\n    if isinstance(obj, Expression):\n        raise TypeError(\"Expression objects are not copiable; reconstruct via pd.col\")\n    import copy\n    return copy.copy(obj)","typeGuard":"from pandas.core.col import Expression\n\ndef is_copyable(obj) -> bool:\n    return not isinstance(obj, Expression)","tryCatchPattern":"import copy\ntry:\n    obj2 = copy.copy(obj)\nexcept TypeError as e:\n    if 'not copiable' in str(e):\n        obj2 = pd.col(obj._repr_col_name)  # reconstruct from stored name\n    else:\n        raise","preventionTips":["Do not store Expressions in structures that get shallow-copied.","Reconstruct Expressions via pd.col(name) instead of copying.","Evaluate to a Series before passing to copy-heavy pipelines."],"tags":["expression","pd-col","copy","deferred","typeerror"],"analyzedSha":"71959b8cb9b2459c16e14b34f28b178ccfe14735","analyzedAt":"2026-08-07T01:30:20.476Z","schemaVersion":2},"datasetVersion":"2026-08-07T03:17:09.362Z"}