Persistent/Functional/Immutable data structures for Python
—
Immutable alternatives to Python's built-in collections that use structural sharing for memory efficiency. All collections provide O(log32 n) performance for most operations and return new instances rather than modifying in-place.
Immutable mapping similar to Python's dict with efficient updates and lookups. Supports all standard mapping operations plus additional persistent-specific methods.
def pmap(initial: Union[Mapping[KT, VT], Iterable[Tuple[KT, VT]]] = {}, pre_size: int = 0) -> PMap[KT, VT]:
"""
Create a persistent map from a mapping or iterable of key-value pairs.
Parameters:
- initial: Initial mapping or iterable of (key, value) pairs
- pre_size: Expected size for optimization (optional)
Returns:
PMap instance
"""
def m(**kwargs) -> PMap[str, Any]:
"""
Create a persistent map from keyword arguments.
Returns:
PMap instance with kwargs as key-value pairs
"""
class PMap:
def get(self, key, default=None):
"""Get value for key, return default if key not found."""
def set(self, key, value) -> 'PMap':
"""Return new PMap with key set to value."""
def remove(self, key) -> 'PMap':
"""Return new PMap without key. Raises KeyError if key missing."""
def discard(self, key) -> 'PMap':
"""Return new PMap without key. Silent if key missing."""
def update(self, *mappings) -> 'PMap':
"""Return new PMap with items from other mappings."""
def update_with(self, update_fn, *mappings) -> 'PMap':
"""Return new PMap updated with merge function for conflicts."""
def evolver(self) -> 'PMapEvolver':
"""Return mutable-like interface for efficient batch updates."""
def transform(self, *transformations) -> 'PMap':
"""Apply path-based transformations to nested structure."""
def keys(self) -> 'PSet':
"""Return PSet of keys."""
def values(self) -> 'PMapValues':
"""Return view of values."""
def items(self) -> 'PMapItems':
"""Return view of key-value pairs."""
def copy(self) -> 'PMap':
"""Return self (immutable, so copy is identity)."""Immutable sequence similar to Python's list with efficient append, random access, and slicing operations.
def pvector(iterable: Iterable = ()) -> PVector:
"""
Create a persistent vector from an iterable.
Parameters:
- iterable: Items to include in vector
Returns:
PVector instance
"""
def v(*elements) -> PVector:
"""
Create a persistent vector from arguments.
Returns:
PVector containing the arguments as elements
"""
class PVector:
def append(self, element) -> 'PVector':
"""Return new PVector with element appended."""
def extend(self, iterable) -> 'PVector':
"""Return new PVector with elements from iterable appended."""
def set(self, index: int, value) -> 'PVector':
"""Return new PVector with element at index replaced."""
def mset(self, *args) -> 'PVector':
"""Multi-set: mset(index1, val1, index2, val2, ...)"""
def delete(self, index: int, stop: int = None) -> 'PVector':
"""Return new PVector with elements removed."""
def remove(self, value) -> 'PVector':
"""Return new PVector with first occurrence of value removed."""
def evolver(self) -> 'PVectorEvolver':
"""Return mutable-like interface for efficient batch updates."""
def transform(self, *transformations) -> 'PVector':
"""Apply path-based transformations to nested structure."""
def tolist(self) -> list:
"""Convert to Python list."""
def index(self, value, start: int = 0, stop: int = None) -> int:
"""Return index of first occurrence of value."""
def count(self, value) -> int:
"""Return number of occurrences of value."""Immutable set with efficient membership testing, set operations, and updates.
def pset(iterable: Iterable = (), pre_size: int = 8) -> PSet:
"""
Create a persistent set from an iterable.
Parameters:
- iterable: Items to include in set
- pre_size: Expected size for optimization
Returns:
PSet instance
"""
def s(*elements) -> PSet:
"""
Create a persistent set from arguments.
Returns:
PSet containing the arguments as elements
"""
class PSet:
def add(self, element) -> 'PSet':
"""Return new PSet with element added."""
def update(self, iterable) -> 'PSet':
"""Return new PSet with elements from iterable added."""
def remove(self, element) -> 'PSet':
"""Return new PSet without element. Raises KeyError if missing."""
def discard(self, element) -> 'PSet':
"""Return new PSet without element. Silent if missing."""
def evolver(self) -> 'PSetEvolver':
"""Return mutable-like interface for efficient batch updates."""
def union(self, *others) -> 'PSet':
"""Return new PSet with elements from all sets."""
def intersection(self, *others) -> 'PSet':
"""Return new PSet with elements common to all sets."""
def difference(self, *others) -> 'PSet':
"""Return new PSet with elements not in other sets."""
def symmetric_difference(self, other) -> 'PSet':
"""Return new PSet with elements in either set but not both."""
def issubset(self, other) -> bool:
"""Test whether every element is in other."""
def issuperset(self, other) -> bool:
"""Test whether every element in other is in this set."""
def isdisjoint(self, other) -> bool:
"""Return True if sets have no elements in common."""
def copy(self) -> 'PSet':
"""Return self (immutable, so copy is identity)."""Immutable multiset (bag) that allows duplicate elements and tracks element counts.
def pbag(elements: Iterable) -> PBag:
"""
Create a persistent bag from an iterable.
Parameters:
- elements: Items to include (duplicates allowed)
Returns:
PBag instance
"""
def b(*elements) -> PBag:
"""
Create a persistent bag from arguments.
Returns:
PBag containing the arguments as elements
"""
class PBag:
def add(self, element) -> 'PBag':
"""Return new PBag with element added (increments count)."""
def remove(self, element) -> 'PBag':
"""Return new PBag with one occurrence of element removed."""
def count(self, element) -> int:
"""Return number of occurrences of element."""
def update(self, iterable) -> 'PBag':
"""Return new PBag with elements from iterable added."""Immutable singly-linked list optimized for prepending operations and functional programming patterns.
def plist(iterable: Iterable = (), reverse: bool = False) -> PList:
"""
Create a persistent linked list from an iterable.
Parameters:
- iterable: Items to include
- reverse: If True, reverse the order
Returns:
PList instance
"""
def l(*elements) -> PList:
"""
Create a persistent linked list from arguments.
Returns:
PList containing the arguments as elements
"""
class PList:
def cons(self, element) -> 'PList':
"""Return new PList with element prepended."""
def mcons(self, iterable) -> 'PList':
"""Return new PList with elements from iterable prepended."""
@property
def first(self):
"""First element of the list."""
@property
def rest(self) -> 'PList':
"""PList of remaining elements after first."""
def reverse(self) -> 'PList':
"""Return new PList in reverse order."""
def remove(self, element) -> 'PList':
"""Return new PList with first occurrence of element removed."""
def split(self, index: int) -> tuple:
"""Return tuple of (left, right) PLists split at index."""Immutable double-ended queue with efficient operations at both ends and optional maximum length.
def pdeque(iterable: Iterable = None, maxlen: int = None) -> PDeque:
"""
Create a persistent deque from an iterable.
Parameters:
- iterable: Items to include
- maxlen: Maximum length (None for unlimited)
Returns:
PDeque instance
"""
def dq(*elements) -> PDeque:
"""
Create a persistent deque from arguments.
Returns:
PDeque containing the arguments as elements
"""
class PDeque:
def append(self, element) -> 'PDeque':
"""Return new PDeque with element appended to right."""
def appendleft(self, element) -> 'PDeque':
"""Return new PDeque with element prepended to left."""
def extend(self, iterable) -> 'PDeque':
"""Return new PDeque with elements from iterable appended."""
def extendleft(self, iterable) -> 'PDeque':
"""Return new PDeque with elements from iterable prepended."""
def pop(self, count: int = 1) -> 'PDeque':
"""Return new PDeque with count elements removed from right."""
def popleft(self, count: int = 1) -> 'PDeque':
"""Return new PDeque with count elements removed from left."""
@property
def left(self):
"""Leftmost element."""
@property
def right(self):
"""Rightmost element."""
@property
def maxlen(self) -> int:
"""Maximum length (None if unlimited)."""
def rotate(self, steps: int) -> 'PDeque':
"""Return new PDeque rotated by steps."""
def reverse(self) -> 'PDeque':
"""Return new PDeque in reverse order."""
def remove(self, element) -> 'PDeque':
"""Return new PDeque with first occurrence of element removed."""Convenient single-character aliases for creating collections:
def m(**kwargs) -> PMap[str, Any]: ... # pmap from keyword arguments
def v(*elements: T) -> PVector[T]: ... # pvector from arguments
def s(*elements: T) -> PSet[T]: ... # pset from arguments
def b(*elements: T) -> PBag[T]: ... # pbag from arguments
def l(*elements: T) -> PList[T]: ... # plist from arguments
def dq(*elements: T) -> PDeque[T]: ... # pdeque from argumentsAll main collections provide .evolver() methods returning mutable-like interfaces for efficient batch updates:
class PMapEvolver:
def __setitem__(self, key, value) -> None: ...
def __delitem__(self, key) -> None: ...
def set(self, key, value) -> 'PMapEvolver': ...
def remove(self, key) -> 'PMapEvolver': ...
def is_dirty(self) -> bool: ...
def persistent(self) -> PMap: ...
class PVectorEvolver:
def __setitem__(self, index: int, value) -> None: ...
def __delitem__(self, index: int) -> None: ...
def append(self, value) -> 'PVectorEvolver': ...
def extend(self, iterable) -> 'PVectorEvolver': ...
def set(self, index: int, value) -> 'PVectorEvolver': ...
def delete(self, value) -> 'PVectorEvolver': ...
def is_dirty(self) -> bool: ...
def persistent(self) -> PVector: ...
class PSetEvolver:
def add(self, element) -> 'PSetEvolver': ...
def remove(self, element) -> 'PSetEvolver': ...
def is_dirty(self) -> bool: ...
def persistent(self) -> PSet: ...# Efficient batch updates using evolvers
pm = pmap({'a': 1, 'b': 2})
evolver = pm.evolver()
evolver['c'] = 3
evolver['d'] = 4
del evolver['a']
new_pm = evolver.persistent() # Get immutable result
# Chaining operations
pv = pvector([1, 2, 3])
result = pv.append(4).extend([5, 6]).set(0, 0) # pvector([0, 2, 3, 4, 5, 6])
# Set operations
ps1 = pset([1, 2, 3])
ps2 = pset([3, 4, 5])
union = ps1.union(ps2) # pset([1, 2, 3, 4, 5])
intersection = ps1 & ps2 # pset([3]) - using operatorInstall with Tessl CLI
npx tessl i tessl/pypi-pyrsistent