{"id":686,"library":"pyrsistent","title":"Persistent/Functional/Immutable Data Structures","description":"Pyrsistent is a Python library providing persistent and immutable data structures, such as lists, dictionaries, and sets, that facilitate functional programming paradigms. Unlike mutable data structures, all operations that would typically modify a `pyrsistent` object instead return a new, updated copy, leaving the original untouched. This simplifies reasoning about program state by eliminating hidden side effects. The library is currently at version 0.20.0, released in October 2023, and is actively maintained.","status":"active","version":"0.20.0","language":"python","source_language":"en","source_url":"https://github.com/tobgu/pyrsistent/","tags":["immutable","functional","data structures","collections","persistent"],"install":[{"cmd":"pip install pyrsistent","lang":"bash","label":"Install latest version"}],"dependencies":[],"imports":[{"note":"For persistent lists; also commonly imported via the 'v' factory function: `from pyrsistent import v`","symbol":"PVector","correct":"from pyrsistent import PVector"},{"note":"For persistent dictionaries; also commonly imported via the 'm' factory function: `from pyrsistent import m`","symbol":"PMap","correct":"from pyrsistent import PMap"},{"note":"For persistent sets; also commonly imported via the 's' factory function: `from pyrsistent import s`","symbol":"PSet","correct":"from pyrsistent import PSet"},{"note":"For immutable objects with fixed fields, similar to named tuples but with PMap capabilities.","symbol":"PRecord","correct":"from pyrsistent import PRecord"},{"note":"Recursively converts mutable Python collections to their pyrsistent counterparts.","symbol":"freeze","correct":"from pyrsistent import freeze"},{"note":"Recursively converts pyrsistent collections back to mutable Python collections.","symbol":"thaw","correct":"from pyrsistent import thaw"}],"quickstart":{"code":"from pyrsistent import pvector, pmap, PRecord\n\n# Create a persistent vector (list-like)\nv1 = pvector([1, 2, 3])\nv2 = v1.append(4) # Returns a new pvector, v1 remains unchanged\nv3 = v2.set(1, 5)  # Replaces element at index 1\n\nprint(f\"Original vector: {v1}\") # Expected: pvector([1, 2, 3])\nprint(f\"Appended vector: {v2}\") # Expected: pvector([1, 2, 3, 4])\nprint(f\"Modified vector: {v3}\") # Expected: pvector([1, 5, 3, 4])\n\n# Create a persistent map (dict-like)\nm1 = pmap({'a': 1, 'b': 2})\nm2 = m1.set('c', 3) # Returns a new pmap, m1 remains unchanged\nm3 = m2.set('a', 5) # Updates value for key 'a'\n\nprint(f\"Original map: {m1}\") # Expected: pmap({'a': 1, 'b': 2})\nprint(f\"Appended map: {m2}\") # Expected: pmap({'a': 1, 'b': 2, 'c': 3})\nprint(f\"Modified map: {m3}\") # Expected: pmap({'a': 5, 'b': 2, 'c': 3})\n\n# Define a persistent record\nclass User(PRecord):\n    name = None\n    age = None\n\nuser1 = User(name='Alice', age=30)\nuser2 = user1.set(age=31) # Returns a new User record\n\nprint(f\"Original user: {user1}\") # Expected: User(name='Alice', age=30)\nprint(f\"Updated user: {user2}\") # Expected: User(name='Alice', age=31)\n","lang":"python","description":"This quickstart demonstrates the creation and immutable 'evolution' of `PVector` (list-like), `PMap` (dict-like), and `PRecord` (object-like) instances. Operations like `append`, `set`, or `discard` always return a new instance with the changes, ensuring the original data structure's integrity."},"warnings":[{"fix":"Review calls to `freeze()` and `thaw()`. If deep recursion is undesired, pass `strict=False` (e.g., `freeze(my_pyrsistent_obj, strict=False)`).","message":"The behavior of `freeze` and `thaw` functions changed in a past update (related to issue #209). They now recursively convert `pyrsistent` data structures into Python built-in types and vice-versa. To retain the *old* (less recursive) behavior, you must explicitly pass `strict=False`.","severity":"breaking","affected_versions":"<=0.20.0 (change was introduced prior to 0.20.0, affects users migrating from older versions)"},{"fix":"Replace `PMap.remove(key)` with `PMap.discard(key)` if you want to avoid `KeyError` when a key might not exist, or ensure you handle the `KeyError` if `remove` is used.","message":"The behavior of `PMap.remove()` was changed in version 0.7.0. It now raises a `KeyError` if the element to be removed is not present. The `PMap.discard()` method was introduced as an alternative that returns the original `PMap` instance if the element is not found, aligning with `PSet`'s behavior.","severity":"breaking","affected_versions":">=0.7.0"},{"fix":"Replace calls to `merge()` with `update()` and `merge_with()` with `update_with()`.","message":"The methods `PMap.merge()` and `PMap.merge_with()` were deprecated and subsequently removed. They were renamed to `PMap.update()` and `PMap.update_with()` respectively.","severity":"breaking","affected_versions":"<1.0 (deprecated before 0.6.3, removed by 1.0)"},{"fix":"Always assign the result of modification operations to a new variable or back to the original variable to capture the new state (e.g., `my_vec = my_vec.append(item)`). Understand that you are always creating new versions, not altering existing ones.","message":"Pyrsistent data structures are fundamentally immutable. Any method that appears to 'modify' a structure (e.g., `append`, `set`, `remove`) actually returns a *new* instance with the changes, leaving the original unchanged. Attempting to treat them as mutable will lead to unexpected results where the original object appears not to have changed.","severity":"gotcha","affected_versions":"All versions"},{"fix":"Wrap sequences of temporary mutations in an evolver: `with my_pvector.evolver() as e: e[idx] = val; e.append(item); new_pvector = e.persistent()`.","message":"For scenarios requiring many sequential updates to a `pyrsistent` collection where intermediate states are not needed, using an 'Evolver' (`pvector.evolver()`, `pmap.evolver()`, `pset.evolver()`) can be more efficient. Evolvers provide a mutable view for temporary, batch updates before finalizing to a new persistent structure.","severity":"gotcha","affected_versions":"All versions"},{"fix":"Ensure all fields you intend to use with a `PRecord` are explicitly declared in its class definition using `field()` or `_fields`. For example: `class MyRecord(PRecord): name = field(); age = field()`.","message":"PRecords require fields to be explicitly defined as part of their class definition. Attempting to initialize or set a field that was not declared will raise an `AttributeError`.","severity":"gotcha","affected_versions":"All versions"},{"fix":"Ensure that all fields intended to be set during `PRecord` instantiation are explicitly declared in the `PRecord` subclass using `PRecord.field()`. Alternatively, review the instantiation call to only pass declared fields.","message":"PRecord instances must be initialized only with fields explicitly defined using `PRecord.field()` in the subclass. Passing keyword arguments for fields not declared in the `PRecord` definition will raise an `AttributeError`.","severity":"breaking","affected_versions":"All versions"}],"env_vars":null,"last_verified":"2026-05-12T17:49:08.102Z","next_check":"2026-06-26T00:00:00.000Z","problems":[{"fix":"Use the `set` method, which returns a new `pvector` with the updated value at the specified index. Example: `new_pv = pv.set(1, 10)`","cause":"Users attempt to modify a `pvector` in-place using item assignment (`pv[idx] = value`), which is not allowed because `pvector` is an immutable data structure.","error":"TypeError: 'pvector' object does not support item assignment"},{"fix":"Use the `set` method, which returns a new `pmap` with the updated value for the specified key. Example: `new_pm = pm.set('b', 20)`","cause":"Users attempt to modify a `pmap` in-place using item assignment (`pm[key] = value`), which is not allowed because `pmap` is an immutable data structure.","error":"TypeError: 'pmap' object does not support item assignment"},{"fix":"Use the `append` method of `pvector`, which returns a new `pvector` with the added element. Example: `new_pv = pv.append(3)`","cause":"Users try to use mutable list methods like `append` in-place on a `pvector` object, which is immutable and returns new instances on modification.","error":"AttributeError: 'pvector' object has no attribute 'append'"},{"fix":"To check for membership, use the `in` operator (e.g., `if value in ps:`). If indexed access is required, convert the `pset` to a list first (e.g., `list(ps)[0]`).","cause":"Users attempt to access elements of a `pset` using indexing (`ps[index]`), which is not supported as sets are unordered and do not allow indexed access.","error":"TypeError: 'pset' object is not subscriptable"}],"ecosystem":"pypi","meta_description":null,"install_score":100,"install_tag":"verified","quickstart_score":0,"quickstart_tag":"stale","pypi_latest":"0.20.0","install_checks":{"last_tested":"2026-05-12","tag":"verified","tag_description":"installs cleanly on critical runtimes, fast import, recently tested","results":[{"runtime":"python:3.10-alpine","python_version":"3.10","os_libc":"alpine (musl)","variant":" $EXIT -eq 0 ","exit_code":0,"wheel_type":"wheel","failure_reason":null,"install_time_s":null,"import_time_s":0.03,"mem_mb":1.8,"disk_size":"18.2M"},{"runtime":"python:3.10-alpine","python_version":"3.10","os_libc":"alpine (musl)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.03,"mem_mb":1.8,"disk_size":"18.2M"},{"runtime":"python:3.10-slim","python_version":"3.10","os_libc":"slim (glibc)","variant":" $EXIT -eq 0 ","exit_code":0,"wheel_type":"wheel","failure_reason":null,"install_time_s":1.6,"import_time_s":0.02,"mem_mb":1.9,"disk_size":"19M"},{"runtime":"python:3.10-slim","python_version":"3.10","os_libc":"slim (glibc)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.02,"mem_mb":1.9,"disk_size":"19M"},{"runtime":"python:3.11-alpine","python_version":"3.11","os_libc":"alpine (musl)","variant":" $EXIT -eq 0 ","exit_code":0,"wheel_type":"wheel","failure_reason":null,"install_time_s":null,"import_time_s":0.06,"mem_mb":2.1,"disk_size":"20.1M"},{"runtime":"python:3.11-alpine","python_version":"3.11","os_libc":"alpine (musl)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.07,"mem_mb":2.1,"disk_size":"20.1M"},{"runtime":"python:3.11-slim","python_version":"3.11","os_libc":"slim (glibc)","variant":" $EXIT -eq 0 ","exit_code":0,"wheel_type":"wheel","failure_reason":null,"install_time_s":1.6,"import_time_s":0.05,"mem_mb":2.1,"disk_size":"21M"},{"runtime":"python:3.11-slim","python_version":"3.11","os_libc":"slim (glibc)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.05,"mem_mb":2.1,"disk_size":"21M"},{"runtime":"python:3.12-alpine","python_version":"3.12","os_libc":"alpine (musl)","variant":" $EXIT -eq 0 ","exit_code":0,"wheel_type":"wheel","failure_reason":null,"install_time_s":null,"import_time_s":0.06,"mem_mb":2,"disk_size":"11.9M"},{"runtime":"python:3.12-alpine","python_version":"3.12","os_libc":"alpine (musl)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.06,"mem_mb":2,"disk_size":"11.9M"},{"runtime":"python:3.12-slim","python_version":"3.12","os_libc":"slim (glibc)","variant":" $EXIT -eq 0 ","exit_code":0,"wheel_type":"wheel","failure_reason":null,"install_time_s":1.5,"import_time_s":0.06,"mem_mb":2,"disk_size":"13M"},{"runtime":"python:3.12-slim","python_version":"3.12","os_libc":"slim (glibc)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.06,"mem_mb":2,"disk_size":"13M"},{"runtime":"python:3.13-alpine","python_version":"3.13","os_libc":"alpine (musl)","variant":" $EXIT -eq 0 ","exit_code":0,"wheel_type":"wheel","failure_reason":null,"install_time_s":null,"import_time_s":0.05,"mem_mb":2.3,"disk_size":"11.7M"},{"runtime":"python:3.13-alpine","python_version":"3.13","os_libc":"alpine (musl)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.05,"mem_mb":2.3,"disk_size":"11.5M"},{"runtime":"python:3.13-slim","python_version":"3.13","os_libc":"slim (glibc)","variant":" $EXIT -eq 0 ","exit_code":0,"wheel_type":"wheel","failure_reason":null,"install_time_s":1.5,"import_time_s":0.05,"mem_mb":2.1,"disk_size":"12M"},{"runtime":"python:3.13-slim","python_version":"3.13","os_libc":"slim (glibc)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.05,"mem_mb":2.1,"disk_size":"12M"},{"runtime":"python:3.9-alpine","python_version":"3.9","os_libc":"alpine (musl)","variant":" $EXIT -eq 0 ","exit_code":0,"wheel_type":"wheel","failure_reason":null,"install_time_s":null,"import_time_s":0.03,"mem_mb":1.8,"disk_size":"17.7M"},{"runtime":"python:3.9-alpine","python_version":"3.9","os_libc":"alpine (musl)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.03,"mem_mb":1.8,"disk_size":"17.7M"},{"runtime":"python:3.9-slim","python_version":"3.9","os_libc":"slim (glibc)","variant":" $EXIT -eq 0 ","exit_code":0,"wheel_type":"wheel","failure_reason":null,"install_time_s":1.8,"import_time_s":0.02,"mem_mb":1.8,"disk_size":"18M"},{"runtime":"python:3.9-slim","python_version":"3.9","os_libc":"slim (glibc)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.03,"mem_mb":1.8,"disk_size":"18M"}]},"quickstart_checks":{"last_tested":"2026-04-24","tag":"stale","tag_description":"widespread failures or data too old to trust","results":[{"runtime":"python:3.10-alpine","exit_code":1},{"runtime":"python:3.10-slim","exit_code":1},{"runtime":"python:3.11-alpine","exit_code":1},{"runtime":"python:3.11-slim","exit_code":1},{"runtime":"python:3.12-alpine","exit_code":1},{"runtime":"python:3.12-slim","exit_code":1},{"runtime":"python:3.13-alpine","exit_code":1},{"runtime":"python:3.13-slim","exit_code":1},{"runtime":"python:3.9-alpine","exit_code":1},{"runtime":"python:3.9-slim","exit_code":1}]}}