| 275 | |
| 276 | |
| 277 | def custom_hash(obj): |
| 278 | # Compute a hash for various types of objects, including unhashable ones. |
| 279 | # This may not be collision-free. For example, hash(-1) is same as hash(-2). |
| 280 | # We use dict to resolve collisions in ConstantParams. |
| 281 | |
| 282 | # Handle basic types |
| 283 | if isinstance(obj, (int, float, str, bool, bytes)): |
| 284 | return hash(obj) |
| 285 | |
| 286 | # Handle sequences (like list, tuple, set, frozenset) |
| 287 | if isinstance(obj, (Sequence, frozenset, set)): |
| 288 | type_id_map = {list: 1, tuple: 2, frozenset: 3, set: 4} |
| 289 | type_id = type_id_map.get(type(obj), 0) |
| 290 | return hash((type_id, *tuple(custom_hash(item) for item in obj))) |
| 291 | |
| 292 | # Handle mappings (like dict) |
| 293 | if isinstance(obj, Mapping): |
| 294 | type_id = 5 |
| 295 | items_hashed = tuple( |
| 296 | sorted((custom_hash(k), custom_hash(v)) for k, v in obj.items()) |
| 297 | ) |
| 298 | return hash((type_id, *items_hashed)) |
| 299 | |
| 300 | # Fallback: try to use the built-in hash, or use id() if unhashable |
| 301 | try: |
| 302 | return hash(obj) |
| 303 | except TypeError: |
| 304 | return id(obj) |
| 305 | |
| 306 | |
| 307 | @overload |