Created
June 11, 2012 22:36
-
-
Save zacharyvoase/2913143 to your computer and use it in GitHub Desktop.
A smarter Python `slice` object.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class SmartSlice(object): | |
""" | |
A slice object which represents the mapping between old and new indices. | |
Given a slice object, you can determine whether a given index will be | |
present in the sub-list that slice represents. For example: | |
>>> 3 in SmartSlice(2, 5) | |
True | |
>>> 0 in SmartSlice(2, 5) | |
False | |
>>> 5 in SmartSlice(2, 5) | |
False | |
This even works with a `step` argument: | |
>>> 2 in SmartSlice(2, 10, 2) | |
True | |
>>> 4 in SmartSlice(2, 10, 2) | |
True | |
>>> 7 in SmartSlice(2, 10, 2) | |
False | |
You can map indices in the existing list to indices in the sub-list: | |
>>> SmartSlice(2, 10, 2).forward_mapping(2) | |
0 | |
>>> SmartSlice(2, 10, 2).forward_mapping(4) | |
1 | |
>>> SmartSlice(2, 10, 2).forward_mapping(8) | |
3 | |
If an index isn't present in the sub-list, an IndexError will be raised: | |
>>> SmartSlice(2, 10, 2).forward_mapping(5) | |
Traceback (most recent call last): | |
... | |
IndexError: index 5 is not represented in the slice | |
This works for reverse mapping as well (mapping indices in the slice to | |
their original positions): | |
>>> SmartSlice(2, 10, 2).reverse_mapping(0) | |
2 | |
>>> SmartSlice(2, 10, 2).reverse_mapping(1) | |
4 | |
>>> SmartSlice(2, 10, 2).reverse_mapping(3) | |
8 | |
This may also raise an IndexError: | |
>>> SmartSlice(2, 10, 2).reverse_mapping(12) | |
Traceback (most recent call last): | |
... | |
IndexError: the slice has no index 12 | |
If you provide a stop index, you can also reverse-map negative indices: | |
>>> SmartSlice(2, 10, 2).reverse_mapping(-2) | |
6 | |
And determine the length of the slice: | |
>>> len(SmartSlice(2, 10, 2)) | |
4 | |
>>> len(SmartSlice(2, 11, 2)) | |
5 | |
But of course some things fail if you don't have a stop index, because | |
there's no way of calculating them: | |
>>> len(SmartSlice(2, None, 2)) | |
Traceback (most recent call last): | |
... | |
TypeError: slice has no stop, and therefore no length | |
>>> SmartSlice(2, None, 2).reverse_mapping(-2) | |
Traceback (most recent call last): | |
... | |
TypeError: slice has no stop, so does not support negative indexing | |
""" | |
__slots__ = ('start', 'stop', 'step') | |
def __init__(self, *args): | |
if len(args) < 1 or len(args) > 3: | |
raise TypeError("Expects between 1 and 3 arguments") | |
elif len(args) == 1: | |
start, stop, step = None, args[0], None | |
elif len(args) == 2: | |
start, stop, step = args[0], args[1], None | |
elif len(args) == 3: | |
start, stop, step = args[0], args[1], args[2] | |
self.start = start | |
self.stop = stop | |
self.step = step | |
def __contains__(self, i): | |
if i < 0: | |
raise TypeError("Negative indices are not supported for forward mapping") | |
elif i < (self.start or 0): | |
return False | |
elif i >= (self.stop or float('inf')): | |
return False | |
return (i - (self.start or 0)) % (self.step or 1) == 0 | |
def __len__(self): | |
if self.stop is None: | |
raise TypeError("slice has no stop, and therefore no length") | |
return ((self.stop + 1) - (self.start or 0)) // (self.step or 1) | |
def forward_mapping(self, i): | |
"""Map indices in a list to their new indices in the slice.""" | |
if i not in self: | |
raise IndexError("index %d is not represented in the slice" % i) | |
return (i - (self.start or 0)) // (self.step or 1) | |
def reverse_mapping(self, i): | |
"""Map indices in the slice to their old positions.""" | |
if (self.stop is not None) and i >= len(self) and i > 0: | |
raise IndexError("the slice has no index %d" % i) | |
elif i < 0: | |
if self.stop is None: | |
raise TypeError("slice has no stop, so does not support negative indexing") | |
elif -i > len(self): | |
raise IndexError("the slice has no index %d" % i) | |
else: | |
i = len(self) + i | |
return (i * self.step) + (self.start or 0) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment