C++ STL-style containers implemented in Python using the Facade Design Pattern
Project description
PythonSTL - Python Standard Template Library
A Python package that replicates C++ STL-style data structures using the Facade Design Pattern. PythonSTL provides clean, familiar interfaces for developers coming from C++ while maintaining Pythonic best practices.
Features
- C++ STL Compliance: Exact method names and semantics matching C++ STL
- Facade Design Pattern: Clean separation between interface and implementation
- Iterator Support: STL-style iterators (begin, end, rbegin, rend) and Python iteration
- Python Integration: Magic methods (len, bool, contains, repr, eq)
- Type Safety: Full type hints throughout the codebase
- Copy Operations: Deep copy support with copy(), copy(), and deepcopy()
- Comprehensive Documentation: Detailed docstrings with time complexity annotations
- Production Quality: Proper error handling, PEP8 compliance, and extensive testing
- Zero Dependencies: Core package has no external dependencies
📦 Installation
pip install pythonstl
Or install from source:
git clone https://github.com/AnshMNSoni/PythonSTL.git
cd PythonSTL
pip install -e .
Quick Start
from pythonstl import stack, queue, vector, stl_set, stl_map, priority_queue
# Stack (LIFO) - Now with Python magic methods!
s = stack()
s.push(10)
s.push(20)
print(s.top()) # 20
print(len(s)) # 2 - Python len() support
print(bool(s)) # True - Python bool() support
# Vector (Dynamic Array) - With iterators!
v = vector()
v.push_back(100)
v.push_back(200)
v.push_back(300)
v.reserve(1000) # Pre-allocate capacity
print(len(v)) # 3
print(200 in v) # True - Python 'in' operator
# Iterate using STL-style iterators
for elem in v.begin():
print(elem)
# Or use Python iteration
for elem in v:
print(elem)
# Set (Unique Elements) - With magic methods
s = stl_set()
s.insert(5)
s.insert(10)
print(5 in s) # True
print(len(s)) # 2
# Map (Key-Value Pairs) - With iteration
m = stl_map()
m.insert("key1", 100)
m.insert("key2", 200)
print("key1" in m) # True
for key, value in m:
print(f"{key}: {value}")
# Priority Queue - With comparator support
pq_max = priority_queue(comparator="max") # Max-heap (default)
pq_min = priority_queue(comparator="min") # Min-heap
pq_max.push(30)
pq_max.push(10)
pq_max.push(20)
print(pq_max.top()) # 30
Data Structures
Stack
LIFO (Last-In-First-Out) container adapter.
Methods:
push(value)- Add element to toppop()- Remove top elementtop()- Access top elementempty()- Check if emptysize()- Get number of elementscopy()- Create deep copy
Python Integration:
len(s)- Get sizebool(s)- Check if non-emptyrepr(s)- String representations1 == s2- Equality comparison
Queue
FIFO (First-In-First-Out) container adapter.
Methods:
push(value)- Add element to backpop()- Remove front elementfront()- Access front elementback()- Access back elementempty()- Check if emptysize()- Get number of elementscopy()- Create deep copy
Python Integration:
len(q)- Get sizebool(q)- Check if non-emptyrepr(q)- String representationq1 == q2- Equality comparison
Vector
Dynamic array with capacity management.
Methods:
push_back(value)- Add element to endpop_back()- Remove last elementat(index)- Access element with bounds checkinginsert(position, value)- Insert element at positionerase(position)- Remove element at positionclear()- Remove all elementsreserve(capacity)- Pre-allocate capacityshrink_to_fit()- Reduce capacity to sizesize()- Get number of elementscapacity()- Get current capacityempty()- Check if emptybegin()- Get forward iteratorend()- Get end iteratorrbegin()- Get reverse iteratorrend()- Get reverse end iteratorcopy()- Create deep copy
Python Integration:
len(v)- Get sizebool(v)- Check if non-emptyvalue in v- Check if value existsrepr(v)- String representationv1 == v2- Equality comparisonv1 < v2- Lexicographic comparisonfor elem in v- Python iteration
Set
Associative container storing unique elements.
Methods:
insert(value)- Add elementerase(value)- Remove elementfind(value)- Check if element existsempty()- Check if emptysize()- Get number of elementsbegin()- Get iteratorend()- Get end iteratorcopy()- Create deep copy
Python Integration:
len(s)- Get sizebool(s)- Check if non-emptyvalue in s- Check if value existsrepr(s)- String representations1 == s2- Equality comparisonfor elem in s- Python iteration
Map
Associative container storing key-value pairs.
Methods:
insert(key, value)- Add or update key-value pairerase(key)- Remove key-value pairfind(key)- Check if key existsat(key)- Access value by keyempty()- Check if emptysize()- Get number of pairsbegin()- Get iteratorend()- Get end iteratorcopy()- Create deep copy
Python Integration:
len(m)- Get sizebool(m)- Check if non-emptykey in m- Check if key existsrepr(m)- String representationm1 == m2- Equality comparisonfor key, value in m- Python iteration
Priority Queue
Container adapter providing priority-based access.
Methods:
push(value)- Insert elementpop()- Remove top elementtop()- Access top elementempty()- Check if emptysize()- Get number of elementscopy()- Create deep copy
Comparator Support:
priority_queue(comparator="max")- Max-heap (default)priority_queue(comparator="min")- Min-heap
Python Integration:
len(pq)- Get sizebool(pq)- Check if non-emptyrepr(pq)- String representationpq1 == pq2- Equality comparison
Time Complexity Reference
| Container | Operation | Complexity |
|---|---|---|
| Stack | push() | O(1) amortized |
| pop() | O(1) | |
| top() | O(1) | |
| Queue | push() | O(1) |
| pop() | O(1) | |
| front() / back() | O(1) | |
| Vector | push_back() | O(1) amortized |
| pop_back() | O(1) | |
| at() | O(1) | |
| insert() | O(n) | |
| erase() | O(n) | |
| reserve() | O(1) | |
| shrink_to_fit() | O(1) | |
| Set | insert() | O(1) average |
| erase() | O(1) average | |
| find() | O(1) average | |
| Map | insert() | O(1) average |
| erase() | O(1) average | |
| find() | O(1) average | |
| at() | O(1) average | |
| Priority Queue | push() | O(log n) |
| pop() | O(log n) | |
| top() | O(1) |
🏗️ Architecture
PythonSTL follows the Facade Design Pattern with three layers:
-
Core Layer (
pythonstl/core/)- Base classes and type definitions
- Custom exceptions
- Iterator classes
-
Implementation Layer (
pythonstl/implementations/)- Private implementation classes (prefixed with
_) - Efficient use of Python built-ins
- Not intended for direct user access
- Private implementation classes (prefixed with
-
Facade Layer (
pythonstl/facade/)- Public-facing classes
- Clean, STL-compliant API
- Delegates to implementation layer
This architecture ensures:
- Encapsulation: Internal implementation is hidden
- Maintainability: Easy to modify internals without breaking API
- Testability: Each layer can be tested independently
Thread Safety
Important: PythonSTL containers are NOT thread-safe by default. If you need to use them in a multi-threaded environment, you must provide your own synchronization (e.g., using threading.Lock).
import threading
from pythonstl import stack
s = stack()
lock = threading.Lock()
def thread_safe_push(value):
with lock:
s.push(value)
Design Decisions
Why Facade Pattern?
- Clean API: Users interact with simple, well-defined interfaces
- Flexibility: Internal implementation can change without affecting users
- Type Safety: Facade layer enforces type contracts
- Error Handling: Consistent error messages across all containers
Why STL Naming?
- Familiarity: C++ developers can use PythonSTL immediately
- Consistency: Predictable method names across containers
- Documentation: Extensive C++ STL documentation applies
Python Integration
Full Python integration while maintaining STL compatibility:
- Magic methods for natural Python usage
- Iterator protocol support
- Copy protocol support
- Maintains backward compatibility
📊 Performance Benchmarks
PythonSTL includes a compiled Rust backend (built with PyO3 and Maturin) for high-performance operations, alongside pure-Python fallbacks. Below are the actual performance comparison results against pure-Python and native C++ (compiled with g++ -O3).
1. Containers Performance (50,000 Operations)
| Container Class | Pure Python | Python + Rust | Speedup Status | Design / Algorithmic Trade-off |
|---|---|---|---|---|
| Stack | 0.2324s | 0.1581s | 1.47x faster | Linear stack operations. Limited by FFI call overhead. |
| Queue | 0.2428s | 0.1608s | 1.51x faster | FIFO operations. Limited by FFI call overhead. |
| Vector | 0.0041s | 0.0034s | 1.20x faster | Push_back & random access indices. Limited by FFI. |
| Set | 0.0216s | 0.1111s | 0.19x faster | Sorted Set vs Unordered Hash Set (replicates C++ B-Tree structure) |
| Map | 0.0389s | 0.1959s | 0.20x faster | Sorted Map vs Unordered Hash Map (replicates C++ B-Tree structure) |
| Priority Queue | 0.0764s | 0.0959s | 0.80x faster | Custom binary heap vs. C-optimized heapq module. |
- Sorted Trees vs. Hash Tables: Python's native
setanddictare highly optimized $O(1)$ hash tables written in C. PythonSTL sets/maps replicate C++'sstd::set/std::mapusing sorted trees (BTreeSet/BTreeMap), which run in $O(\log N)$ and sort keys. - FFI overhead: Storing arbitrary Python objects in Rust requires acquiring the GIL and calling back into the Python VM for comparisons, creating high FFI boundaries.
2. Algorithms Suite
| Algorithm Name | Pure Python (Middle Pivot) | Python + Rust | Pure C++ (O3) | Rust Speedup | Design & FFI Insights |
|---|---|---|---|---|---|
| next_permutation | 0.2413s | 0.1428s | 0.0000s | 1.7x | Lexicographical rearrangement. Limited by FFI conversions. |
| nth_element | 0.0064s | 0.0052s | 0.0000s | 1.2x | Quickselect median find. (Previously 70.85s before optimization). |
| partition | 0.0227s | 0.0164s | 0.0003s | 1.4x | Lambda-predicate partitioning. Dominated by FFI callback overhead. |
- Algorithmic Pivot Vulnerabilities: A naive Lomuto partition (
pivot = arr[right]) causes $O(N^2)$ worst-case time on already-sorted or reversed lists (taking 70.85s). By switching PythonSTL to a middle-pivot (arr[mid]), we restore $O(N)$ average time (0.0064s).
3. Binary Search (5,000 Queries on 1,000,000 elements)
| Search Mode / Comparator | Pure Python | Python + Rust | Pure C++ (O3) | Rust Speedup | Systems & Design Insights |
|---|---|---|---|---|---|
Standard (< comparison) |
0.0182s | 0.0034s | 0.0000s | 5.3x | Preserves $O(\log N)$ via direct list indexing. |
| Custom Comparator (lambda) | 0.0180s | 0.0078s | N/A | 2.3x | Overcomes Python loop overhead despite FFI callbacks. |
- Direct Indexing: Instead of extracting/copying the entire list (an $O(N)$ operation), the Rust backend uses direct GIL-bound indexing (
arr.get_item(mid)), maintaining the strict $O(\log N)$ search complexity.
⚠️ Performance Characteristics & Limitations
While PythonSTL provides powerful optimizations, it is essential to understand the systems-level design characteristics and FFI boundaries when using this library:
-
FFI (Foreign Function Interface) Overhead on Granular Operations:
- For container methods like
stack.push()/pop()orqueue.push()/pop(), the Rust backend is only marginally faster (around 1.3x - 1.5x) than pure Python. - Why: Calling a compiled function from Python space millions of times introduces constant-factor FFI boundary crossing overhead (argument validation, GIL checks, etc.) that dominates the execution time.
- Guidance: The Rust backend excels at computation-intensive algorithms (like
bubble_sortrunning 190x+ faster orbinary_searchrunning 5x+ faster) that cross the FFI boundary only once.
- For container methods like
-
Sorted Trees vs. Hash Tables (Set & Map):
- Rust-backed
stl_setandstl_mapare slower than Python's nativesetanddict. - Why: Python's native sets/dicts are highly optimized unordered hash tables ($O(1)$ average complexity). Replicating C++ STL associative compliance requires sorted trees (Rust's
BTreeSetandBTreeMap), which run in $O(\log N)$ time and execute callbacks into the Python VM for custom comparisons. - Guidance: Use
stl_set/stl_mapwhen you explicitly require keys to be maintained in sorted order (e.g., for range/boundary lookups) matching standard C++ semantics, rather than simple hash-lookup speed.
- Rust-backed
Testing
Run the test suite:
# Install test dependencies
pip install pytest pytest-cov
# Run tests
pytest tests/
# Run with coverage
pytest tests/ --cov=pythonstl --cov-report=html
🛠️ Development
Setup
git clone https://github.com/AnshMNSoni/PythonSTL.git
cd PythonSTL
pip install -e ".[dev]"
Code Quality
# Type checking
mypy pythonstl/
# Linting
flake8 pythonstl/
# Run all checks
pytest && mypy pythonstl/ && flake8 pythonstl/
Note
➡️ The goal is NOT to replace Python built-ins.
➡️ The goal is to provide: 1) Conceptual clarity 2) STL familiarity for C++ developers 3) A structured learning bridge for DSA
📝 License
MIT License - see LICENSE file for details.
🤝 Contributing
Contributions are welcome! Please:
- Fork the repository
- Create a feature branch
- Add tests for new features
- Ensure all tests pass
- Submit a pull request
Thankyou
Contact
- GitHub: @AnshMNSoni
- Issues: GitHub Issues
PythonSTL v1.1.6 - Bringing C++ STL elegance to Python
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 pythonstl-1.1.6.tar.gz.
File metadata
- Download URL: pythonstl-1.1.6.tar.gz
- Upload date:
- Size: 380.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
bd548f54f9ed3f74bc1cb50ab1407e1cfebba2c95d4ffad71ab3abdcc4658c26
|
|
| MD5 |
9ea8b0da73658cb5617015f9e1c92022
|
|
| BLAKE2b-256 |
25e926882905f59fb97fb1aa6c4323f6e64f9eae5efa2a337a02519c8ac2297a
|
File details
Details for the file pythonstl-1.1.6-cp311-cp311-win_amd64.whl.
File metadata
- Download URL: pythonstl-1.1.6-cp311-cp311-win_amd64.whl
- Upload date:
- Size: 217.8 kB
- Tags: CPython 3.11, Windows x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5d591b5fb350723890738c20a50e32608e4e3a271026e4792b36a4ed75b37582
|
|
| MD5 |
fba6fec7b6f7a5a44e27ea077201b2c3
|
|
| BLAKE2b-256 |
32e70d1f7c272e6cbd352ba5e5f78a657d46560fe117a8eb2ade70165b51fa60
|