Skip to main content

An efficient, immutable, type-safe copy-on-write (COW) list implementation for Python 2+.

Project description

COWList 🐄 -- An Immutable, Copy-on-Write List for Python

An efficient, immutable, type-safe copy-on-write (COW) list implementation for Python 2+.

COWList provides a persistent, immutable list-like data structure where all mutating operations return a new instance, allowing shared storage between list versions until modifications are made. This results in efficient memory use, O(1) indexing, and safe concurrency patterns.

Features

  • Immutable: All operations return a new instance.
  • 🧠 Copy-on-Write: Physical storage is shared until a mutation is required.
  • Efficient:
    • O(1) indexing and slicing
    • O(1) appending (if the current instance is contiguous and not a sliced view)
    • O(k) extending (if the current instance is contiguous and not a sliced view)
  • 🧩 Pythonic: Implements the full collections.abc.Sequence protocol. Fully typed.
  • 🔐 Hashable: Suitable for use as dict keys or set elements (when elements are hashable).
  • 🧪 Testable & Extendable: Clean architecture for experimentation or educational purposes.

Installation

pip install cowlist

Usage

from cowlist import COWList

# --- Construction ---
lst = COWList([1, 2, 3])
assert list(lst) == [1, 2, 3]

empty = COWList()
assert list(empty) == []
assert len(empty) == 0

# --- Immutability ---
lst2 = lst.append(4)
assert list(lst) == [1, 2, 3]         # Original unchanged
assert list(lst2) == [1, 2, 3, 4]     # New list with appended value

# --- Indexing and slicing ---
assert lst[0] == 1
assert lst[-1] == 3
assert list(lst[:2]) == [1, 2]

# --- Equality and hashing ---
lst_copy = COWList([1, 2, 3])
assert lst == lst_copy
assert hash(lst) == hash(lst_copy)

# --- Append ---
lst_app = lst.append(9)
assert list(lst_app) == [1, 2, 3, 9]

# --- Insert ---
lst_ins = lst.insert(1, 99)
assert list(lst_ins) == [1, 99, 2, 3]

# --- Extend ---
lst_ext = lst.extend([4, 5])
assert list(lst_ext) == [1, 2, 3, 4, 5]

# --- Delete by index ---
lst_del = lst.delete(1)
assert list(lst_del) == [1, 3]

# --- Delete by slice ---
lst_del_slice = lst.delete(slice(1, 3))
assert list(lst_del_slice) == [1]

# --- Remove value ---
lst_rem = lst.remove(2)
assert list(lst_rem) == [1, 3]

# --- Set by index ---
lst_set = lst.set(1, 42)
assert list(lst_set) == [1, 42, 3]

# --- Set by slice ---
lst_set_slice = lst.set(slice(1, 3), [7, 8])
assert list(lst_set_slice) == [1, 7, 8]

# --- Reverse ---
lst_rev = lst.reverse()
assert list(lst_rev) == [3, 2, 1]

# --- Pop ---
lst_popped, val = lst.pop()
assert list(lst_popped) == [1, 2]
assert val == 3

# --- Clear ---
lst_clear = lst.clear()
assert list(lst_clear) == []
assert lst_clear == COWList()

# --- Repr ---
assert repr(lst) == 'COWList([1, 2, 3])'

# --- Contains ---
assert 2 in lst
assert 5 not in lst

# --- Comparison ---
assert COWList([1, 2]) < COWList([1, 2, 3])
assert COWList([1, 3]) > COWList([1, 2])

How It Works

Internally, COWList maintains:

  • A shared physical list of elements.
  • A logical offset CanonicalRange into that list.

On mutation, if possible, it reuses storage; otherwise, it copies only the parts it needs.

This strategy enables structural sharing and helps maintain immutability while preserving performance.

API Reference

Method Description
__getitem__(self, index_or_slice: Union[int, slice]) -> Union[T, Self] Get element at index or sliced view of the list. O(1)
append(self, value: T) -> Self Return new instance with value appended to the end. O(1) if current instance is contiguous, O(n) if current instance is a sliced view.
extend(self, iterable: Iterable[T]) -> Self, __add__(self, iterable: Iterable[T]) -> Self Return new instance extended with values in iterable. O(k) if current instance is contiguous, O(n + k) if current instance is a sliced view.
insert(self, index: int, value: T) -> Self Return new instance with value inserted at index. O(n)
delete(self, index_or_slice: Union[int, slice]) -> Self Return new instance with element(s) at specified position(s) deleted. O(n)
set(self, index_or_slice: Union[int, slice], value_or_iterable: Union[T, Iterable[T]]) -> Self Return new instance with element(s) at given position(s) replaced with new value(s). O(n)
pop(self, index: int = -1) -> Tuple[Self, T] Return (new instance with item removed, removed item), where removed item is self[i]. O(n)
clear(self) -> Self Return an empty new instance. O(1)
reverse(self) -> Self Return a sliced view of the list with the elements in reverse order. O(1)

Contributing

Contributions are welcome! Please submit pull requests or open issues on the GitHub repository.

License

This project is licensed under the Apache License.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

cowlist-0.1.0a5.tar.gz (10.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

cowlist-0.1.0a5-py2.py3-none-any.whl (11.5 kB view details)

Uploaded Python 2Python 3

File details

Details for the file cowlist-0.1.0a5.tar.gz.

File metadata

  • Download URL: cowlist-0.1.0a5.tar.gz
  • Upload date:
  • Size: 10.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.12.2

File hashes

Hashes for cowlist-0.1.0a5.tar.gz
Algorithm Hash digest
SHA256 1869d52b855d8a95d2a64f60123a9e8b407713fec2002199396c85b85f3f9ce3
MD5 a12ebeb28923f58b0e11cc1b308dcaff
BLAKE2b-256 5a724ccc2c219ec82582425f3dc3c89dedab55783ecacc565aedb7ce97fa8401

See more details on using hashes here.

File details

Details for the file cowlist-0.1.0a5-py2.py3-none-any.whl.

File metadata

  • Download URL: cowlist-0.1.0a5-py2.py3-none-any.whl
  • Upload date:
  • Size: 11.5 kB
  • Tags: Python 2, Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.12.2

File hashes

Hashes for cowlist-0.1.0a5-py2.py3-none-any.whl
Algorithm Hash digest
SHA256 595a7c604995e37df21966c86fc1e106e55a324cd7033f70fbca4853c80e3cd5
MD5 a1d4a59d4b2cc47758cde006bc7e45e6
BLAKE2b-256 776d0048f9f9aa3ac9cd068439ca6b516f7d810ab5502a4ecad171760ca5b64a

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page