{"record":{"id":"e4e2fe636abbc201","repo":"pola-rs/polars","slug":"unhashable-type-expr-consider-hashing-self","errorCode":null,"errorMessage":"unhashable type: 'Expr'\n\nConsider hashing '{self}.meta'.","messagePattern":"unhashable type: 'Expr'\n\nConsider hashing '(.+?)\\.meta'\\.","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"py-polars/src/polars/expr/expr.py","lineNumber":319,"sourceCode":"        return self._pyexpr.to_str()\n\n    def __repr__(self) -> str_:\n        if self._pyexpr is not None:\n            if len(expr_str := self._pyexpr.to_str()) > 30:\n                expr_str = f\"{expr_str[:30]}…\"\n            return f\"<{self.__class__.__name__} [{expr_str!r}] at 0x{id(self):X}>\"\n        else:\n            return \"only during sphinx\"\n\n    def __str__(self) -> str_:\n        if self._pyexpr is not None:\n            return self._pyexpr.to_str()\n        else:\n            return \"only during sphinx\"\n\n    def __hash__(self) -> int:\n        msg = f\"unhashable type: 'Expr'\\n\\nConsider hashing '{self}.meta'.\"\n        raise TypeError(msg)\n\n    def __bool__(self) -> NoReturn:\n        msg = (\n            \"the truth value of an Expr is ambiguous\"\n            \"\\n\\n\"\n            \"You probably got here by using a Python standard library function instead \"\n            \"of the native expressions API.\\n\"\n            \"Here are some things you might want to try:\\n\"\n            \"- instead of `pl.col('a') and pl.col('b')`, use `pl.col('a') & pl.col('b')`\\n\"\n            \"- instead of `pl.col('a') in [y, z]`, use `pl.col('a').is_in([y, z])`\\n\"\n            \"- instead of `max(pl.col('a'), pl.col('b'))`, use `pl.max_horizontal(pl.col('a'), pl.col('b'))`\\n\"\n        )\n        raise TypeError(msg)\n\n    def __abs__(self) -> Expr:\n        return self.abs()\n\n    # operators","sourceCodeStart":301,"sourceCodeEnd":337,"githubUrl":"https://github.com/pola-rs/polars/blob/df599052daf96e7a9cc30a3b0c6bd25d6947e3c0/py-polars/src/polars/expr/expr.py#L301-L337","documentation":"Expr deliberately defines __hash__ to raise TypeError: expressions are lazy AST wrappers without stable hashable identity. The message points at .meta (the MetaExpr introspection view) as the hashable/comparable handle. Dict keys, set members, and `expr in some_dict` membership all hash first, so any of those with an Expr triggers the error. Note `expr in [list]` does not hash and is unaffected.","triggerScenarios":"d[pl.col('a')] = 1; pl.col('a') in {pl.col('b')}; set([pl.col('a'), pl.col('b')]); functools.lru_cache on a function that takes an Expr; using Expr as a field of a frozen dataclass placed in a set.","commonSituations":"Memoising column-transform factories keyed by expression; deduplicating expressions in pipeline builders; putting Exprs into dict-based dispatch tables.","solutions":["Key by the string form: str(expr) (the canonical expression string)","Use expr.meta (MetaExpr) as the message suggests for structural identity/equality","Redesign cache keys to be plain data (column name + parameters) rather than the Expr object"],"exampleFix":"# before\ncache[pl.col('a') + 1] = transform  # TypeError: unhashable\n\n# after\ncache[str(pl.col('a') + 1)] = transform  # stable string key","handlingStrategy":"type-guard","validationCode":"import polars as pl\n\ndef expr_key(e: pl.Expr) -> str:\n    return str(e)  # stable, hashable representation\n\ncache: dict[str, object] = {}\ncache[expr_key(pl.col('a') + 1)] = transform  # never hash the Expr itself","typeGuard":"import polars as pl\nfrom typing import TypeGuard\n\ndef is_polars_expr(x) -> TypeGuard[pl.Expr]:\n    return isinstance(x, pl.Expr)\n\n# guard before dict/set usage\nif is_polars_expr(key):\n    key = str(key)","tryCatchPattern":"try:\n    cache[expr] = f\nexcept TypeError as e:\n    if \"unhashable type: 'Expr'\" in str(e):\n        cache[str(expr)] = f\n    else:\n        raise","preventionTips":["Never use Expr as dict keys or set members; key by str(expr) or expr.meta","Remember `expr in some_dict` hashes (fails) while `expr in some_list` does not","Avoid lru_cache on functions taking Expr arguments"],"tags":["expr","hashing","dict-key","api-misuse"],"backgroundTag":null,"analyzedSha":"df599052daf96e7a9cc30a3b0c6bd25d6947e3c0","analyzedAt":"2026-08-16T12:10:03.978Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}