Pre-release
This release is a pre-release and may not be stable for production use.
canonical-range
A Python implementation of a canonical range (where step != 0, length >= 0, stop == start + length * step) with enhanced functionality compared to Python's built-in range.
Features
- Canonical Representation: Always stores ranges in a consistent mathematical form (
step != 0, length >= 0, stop == start + length * step) - Full Sequence Protocol: Implements all Sequence methods (
__getitem__,__len__, etc.) - Type Safety: Comprehensive type hints throughout
- Memory Efficient: Uses
__slots__for reduced memory footprint
Installation
pip install canonical-range
Basic Usage
from canonical_range import CanonicalRange
def start_stop_step(canonical_range):
return canonical_range.start, canonical_range.stop, canonical_range.step
# Range contains the elements [10, 13, 16, 19]
# Canonicalized to CanonicalRange(10, 22, 3)
r1 = CanonicalRange(10, 20, 3)
assert start_stop_step(r1) == (10, 22, 3)
assert r1.length == 4
# Range contains the elements [10, 13, 16, 19, 22]
# Canonicalized to CanonicalRange(10, 25, 3)
r2 = CanonicalRange(10, 23, 3)
assert start_stop_step(r2) == (10, 25, 3)
assert r2.length == 5
# Range contains the elements [20, 17, 14, 11]
# Canonicalized to CanonicalRange(20, 8, -3)
r3 = CanonicalRange(20, 10, -3)
assert start_stop_step(r3) == (20, 8, -3)
assert r3.length == 4
# Extending ranges
assert repr(CanonicalRange(1, 5, 1).extend(2)) == '(CanonicalRange(1, 7, 1), CanonicalRange(5, 7, 1))'
assert repr(
CanonicalRange(1, 5, 1).extend(0)) == '(CanonicalRange(1, 5, 1), CanonicalRange(5, 5, 1))' # Empty extension
# Range is empty
assert not CanonicalRange(5, 5, 1)
assert not CanonicalRange(5, 5, -1)
assert not CanonicalRange(5, 4, 1)
assert not CanonicalRange(5, 6, -1)
# Indexing and slicing
r4 = CanonicalRange(0, 10, 1)
# Empty slice
assert r4[1:1] == CanonicalRange(1, 1, 1)
assert not r4[1:1]
assert r4[1:0] == CanonicalRange(1, 1, 1)
assert not r4[1:0]
# Range contains the elements [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
assert r4[0] == r4[-10] == 0
assert r4[-1] == r4[9] == 9
assert all(i in r4 for i in range(10))
assert -1 not in r4
assert 10 not in r4
# Sliced range contains the elements [2, 3, 4]
assert start_stop_step(r4[2:5]) == (2, 5, 1)
# Sliced range contains the elements [2, 4]
assert start_stop_step(r4[2:5:2]) == (2, 6, 2)
# Sliced range contains the elements [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
assert start_stop_step(r4[::-1]) == start_stop_step(reversed(r4)) == (9, -1, -1)
assert 9 in r4[::-1]
assert 0 in r4[::-1]
assert -1 not in r4[::-1]
# Sliced range contains the elements [5, 3]
assert start_stop_step(r4[5:2:-2]) == (5, 1, -2)
# For ranges,
# If step > 0,
# If start < -length, it gets clamped up to 0
# If start > length, it gets clamped down to length
# If stop < -length, it gets clamped up to 0
# If stop > length, it gets clamped down to length
# We then do canonicalization on top of this
# Examples:
# range(10)[-14:-16:2] -> range(0, 0, 2)
# range(10)[-14:-12:2] -> range(0, 0, 2)
# range(10)[-14:6:4] -> range(0, 6, 4) -> range(0, 8, 4)
# range(10)[-14:28:21] -> range(0, 10, 21) -> range(0, 21, 21)
# range(10)[14:-14:2] -> range(10, 0, 2) -> range(10, 10, 2)
# range(10)[14:2:2] -> range(10, 2, 2) -> range(10, 10, 2)
# range(10)[14:12:2] -> range(10, 10, 2)
# range(10)[14:16:2] -> range(10, 10, 2)
assert start_stop_step(r4[-14:-16:2]) == (0, 0, 2)
assert start_stop_step(r4[-14:-12:2]) == (0, 0, 2)
assert start_stop_step(r4[-14:6:4]) == (0, 8, 4)
assert start_stop_step(r4[-14:28:21]) == (0, 21, 21)
assert start_stop_step(r4[14:-14:2]) == start_stop_step(r4[14:2:2]) == start_stop_step(r4[14:12:2]) == start_stop_step(r4[14:16:2]) == (10, 10, 2)
# If step < 0,
# If start < -length, it gets clamped up to -1
# If start > length - 1, it gets clamped down to length - 1
# If stop < -length, it gets clamped up to -1
# If stop > length - 1, it gets clamped down to length - 1
# We then do canonicalization on top of this
# Examples:
# range(10)[-14:-16:-2] -> range(-1, -1, -2)
# range(10)[-14:-12:-2] -> range(-1, -1, -2)
# range(10)[-14:6:-2] -> range(-1, 6, -2) -> range(-1, -1, -2)
# range(10)[-14:6:-4] -> range(-1, 6, -4) -> range(-1, -1, -4)
# range(10)[-14:28:-21] -> range(-1, 9, -21) -> range(-1, -1, -21)
# range(10)[14:-14:-2] -> range(9, -1, -2)
# range(10)[14:2:-2] -> range(9, 2, -2) -> range(9, 1, -2)
# range(10)[14:12:-2] -> range(9, 9, -2)
# range(10)[14:16:-2] -> range(9, 9, -2)
assert start_stop_step(r4[-14:-16:-2]) == start_stop_step(r4[-14:-12:-2]) == start_stop_step(r4[-14:6:-2]) == (-1, -1, -2)
assert start_stop_step(r4[-14:6:-4]) == (-1, -1, -4)
assert start_stop_step(r4[-14:28:-21]) == (-1, -1, -21)
assert start_stop_step(r4[14:-14:-2]) == (9, -1, -2)
assert start_stop_step(r4[14:2:-2]) == (9, 1, -2)
assert start_stop_step(r4[14:12:-2]) == start_stop_step(r4[14:16:-2]) == (9, 9, -2)
Contributing
Contributions are welcome! Please submit pull requests or open issues on the GitHub repository.
License
This project is licensed under the MIT License.
Release files for canonical-range 0.1.0a2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| canonical_range-0.1.0a2.tar.gz | 5.7 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| canonical_range-0.1.0a2-py2.py3-none-any.whl | Python 2, Python 3 | none | any | Details |
Total release size: 11.6 kB
Release files / canonical_range-0.1.0a2.tar.gz
| Download URL | canonical_range-0.1.0a2.tar.gz |
|---|---|
| Size | 5.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
bbe4b3126cffc24eaaced33574a5e3d166c7d5132fe441182712f55d3a3252ca
|
|
BLAKE2b-256 checksum How to use checksums |
528a03e2e737907324163f96bf90161d29677d69456a1a0b1fbe3d1912823e23
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.1.0 CPython/3.12.2
|
Release files / canonical_range-0.1.0a2-py2.py3-none-any.whl
| Download URL | canonical_range-0.1.0a2-py2.py3-none-any.whl |
|---|---|
| Size | 5.9 kB |
| Tags | Python 2 Python 3 |
|
SHA-256 checksum How to use checksums |
dc75b71aa8cc87ae882af0db0986939f7be583c6eba7c7a4aaa9ff69c836e234
|
|
BLAKE2b-256 checksum How to use checksums |
93404c96ee5870980eb31d301331ec64cd5858a329064180a9c1b7064c57489c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.1.0 CPython/3.12.2
|