Memory optimized implementations for the main Python Collection ABCs.
Project description
Opticol
Optimized collections (opticol) for Python. This package provides memory-efficient, slot-based implementations of Python's standard collection types:
- Mapping / MutableMapping
- Sequence / MutableSequence
- Set / MutableSet
An example of the insight behind the package: an empty set (on Python 3.14) uses 216 bytes, but an empty object with empty __slots__ uses only 32 bytes. For applications creating thousands of small collections, opticol can dramatically reduce memory usage without API changes. The optimized types fully implement their respective collection ABCs.
When to Use Opticol
Opticol is beneficial when your application:
- Creates many small collections (0-3 elements is the default optimization range)
- Has memory constraints or needs to reduce memory footprint
- Can benefit from transparent optimization without code changes
For general use cases where memory isn't constrained, standard Python collections are perfectly adequate.
Usage
Quick Start (Convenience API)
The simplest way to use opticol is via the convenience functions:
import opticol
# Create optimized collections
optimized_list = opticol.seq([1, 2, 3])
optimized_set = opticol.set({1, 2, 3})
optimized_dict = opticol.mapping({'a': 1, 'b': 2})
# Mutable variants
mutable_list = opticol.mut_seq([1, 2])
mutable_set = opticol.mut_set({1, 2})
mutable_dict = opticol.mut_mapping({'a': 1})
# They implement standard ABCs
from collections.abc import Sequence, MutableSequence
assert isinstance(optimized_list, Sequence)
assert isinstance(mutable_list, MutableSequence)
By default, these functions optimize collections of size 0-3. Collections outside this range are returned unchanged as standard Python types. This method is useful for smaller scripts or applications.
Advanced (Projector API)
For custom optimization strategies or more complex applications, use projectors:
from opticol.projector import OptimizedCollectionProjector
# Create a projector with custom size range
projector = OptimizedCollectionProjector(min_size=0, max_size=5)
# Use it to optimize collections
optimized = projector.seq([1, 2, 3, 4, 5])
You can also create custom projectors by subclassing Projector. PassThroughProjector is a Projector implementation in the library that returns the argument as-is:
from opticol.projector import PassThroughProjector
from collections.abc import Sequence
class SelectiveProjector(PassThroughProjector):
"""Only optimize sequences, pass through everything else."""
def seq(self, seq):
# Your custom optimization logic
return optimized_version(seq)
Projectors are intended to be pluggable DI components which allow for flexible and dynamic policies. Rather than relying on the convenience methods, logic which can benefit from the optimizations can consume a Projector which could be anything from a PassThroughProjector (and falls back to Python defaults) to a custom policy which uses domain specific knowledge to improve memory consumption.
Architecture
Opticol has a three-layer architecture:
- Factory Layer (
opticol.factory): Generates optimized collection classes of arbitrary sizes using metaclasses and__slots__ - Projector Layer (
opticol.projector): The main consumer API which acts as a pluggable policy pattern for different optimization strategies - Convenience Layer (
opticol): Simple functions backed by a default projector instance
This design allows you to use opticol in two ways:
- Quick start: Use the convenience functions at the module level for immediate optimization with sensible defaults
- Advanced: Create custom projectors to fine-tune optimization behavior for your use case
Details
Immutable Collections
Immutable collections (Sequence, Set, Mapping) are optimized for exact sizes. Each element is stored in a dedicated slot:
s = opticol.seq([1, 2])
# Internally: _item0 = 1, _item1 = 2, which is smaller than list([1, 2])
Mutable Collections
Mutable collections support overflow to standard types when exceeding capacity:
m = opticol.mut_seq([1, 2]) # Optimized, uses two slots to start
m.append(3) # Overflowed to use standard list internally
m.append(4) # Still uses standard list internally
m.pop() # Stays as list (no downgrade)
m.pop() # Reverts back to optimized storage
Optimization Propagation
Some collection operations return new instances such as slicing or set intersection or union operations. The convenience layer at the module level will propgate the optimization structure by default as if it were passed through the original optimization function.
seq = opticol.seq([1, 2, 3])
seq = seq[1:2] # Has only one slot internally and technically an instance of a different class.
mut_seq = opticol.mut_seq([1, 2, 3])
mut_seq.extend([4, 5]) # Beyond optimization and overflowed into list internally
sub1 = mut_seq[3:4] # Optimized instance with one slot.
sub2 = mut_seq[:] # A simple list because the length is beyond the optimization point
mut_set = opticol.set({1, 2, 3})
bigger_set = mut_set | {4, 5, 6} # Default python set
smaller_set = mut_set & {2, 3} # Optimized python set for 2 elements
This decision was made so that optimizations are kept as much as possible. The alternative of always providing the python built-in is possible by creating a custom Projector, and may provide slightly better results for runtime and performance in specific applications.
Memory Savings
Below is a table which outlines the memory consumption differences per collection instance. As can be seen, sequence types benefit the least, while mapping, and set types benefit the most. The threshold whereby the savings for each collection type is no longer valuable is quite different and different applications may find different thresholds than those assumed by the convenience functions.
| Collection Type | Length | Default (b) | Optimized (b) | Savings % |
|---|---|---|---|---|
| List | 0 | 56 | 32 | 43 |
| 1 | 64 | 40 | 38 | |
| 2 | 72 | 48 | 33 | |
| 3 | 88 | 56 | 30 | |
| Dict | 0 | 64 | 32 | 50 |
| 1 | 224 | 40 | 82 | |
| 2 | 224 | 48 | 79 | |
| 3 | 224 | 56 | 75 | |
| Set | 0 | 216 | 32 | 85 |
| 1 | 216 | 40 | 81 | |
| 2 | 216 | 48 | 78 | |
| 3 | 216 | 56 | 74 |
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file opticol-0.1.0b2.tar.gz.
File metadata
- Download URL: opticol-0.1.0b2.tar.gz
- Upload date:
- Size: 24.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.9.16 {"installer":{"name":"uv","version":"0.9.16","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0151d3ab460192e2ca39a0e78a1389033181731314027246ab9f3446ef404988
|
|
| MD5 |
72a0ef37d7ca1b6b8149b8b015ef391a
|
|
| BLAKE2b-256 |
edf72ecc41bfe9fcc0d5e76722da82fb7533ba19f2cfd143a008b1daecac4601
|
File details
Details for the file opticol-0.1.0b2-py3-none-any.whl.
File metadata
- Download URL: opticol-0.1.0b2-py3-none-any.whl
- Upload date:
- Size: 17.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.9.16 {"installer":{"name":"uv","version":"0.9.16","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
59c3ddbdf83f005cb87a27151cf6dfd60cc7f884fab03e8a525649646cb79bf9
|
|
| MD5 |
ff8a77b8861c202136fcfeab5eed1601
|
|
| BLAKE2b-256 |
d2c9106d3dc549ceaf127e2631d34fe9374b7d633d826ea774d2bba9893f5d3a
|