Persistent/Functional/Immutable Data Structures
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.
Warnings
- breaking 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`.
- breaking 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.
- breaking The methods `PMap.merge()` and `PMap.merge_with()` were deprecated and subsequently removed. They were renamed to `PMap.update()` and `PMap.update_with()` respectively.
- gotcha 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.
- gotcha 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.
Install
-
pip install pyrsistent
Imports
- PVector
from pyrsistent import PVector
- PMap
from pyrsistent import PMap
- PSet
from pyrsistent import PSet
- PRecord
from pyrsistent import PRecord
- freeze
from pyrsistent import freeze
- thaw
from pyrsistent import thaw
Quickstart
from pyrsistent import pvector, pmap, PRecord
# Create a persistent vector (list-like)
v1 = pvector([1, 2, 3])
v2 = v1.append(4) # Returns a new pvector, v1 remains unchanged
v3 = v2.set(1, 5) # Replaces element at index 1
print(f"Original vector: {v1}") # Expected: pvector([1, 2, 3])
print(f"Appended vector: {v2}") # Expected: pvector([1, 2, 3, 4])
print(f"Modified vector: {v3}") # Expected: pvector([1, 5, 3, 4])
# Create a persistent map (dict-like)
m1 = pmap({'a': 1, 'b': 2})
m2 = m1.set('c', 3) # Returns a new pmap, m1 remains unchanged
m3 = m2.set('a', 5) # Updates value for key 'a'
print(f"Original map: {m1}") # Expected: pmap({'a': 1, 'b': 2})
print(f"Appended map: {m2}") # Expected: pmap({'a': 1, 'b': 2, 'c': 3})
print(f"Modified map: {m3}") # Expected: pmap({'a': 5, 'b': 2, 'c': 3})
# Define a persistent record
class User(PRecord):
name = None
age = None
user1 = User(name='Alice', age=30)
user2 = user1.set(age=31) # Returns a new User record
print(f"Original user: {user1}") # Expected: User(name='Alice', age=30)
print(f"Updated user: {user2}") # Expected: User(name='Alice', age=31)