After considering several alternatives and trying out a few ideas with a modified list object Bengt Richter posted, (Thank You), I think I've found a way to make slice operation (especially far end indexing) symmetrical and more consistent.
So to find out if this is indeed a possibility, it would be nice to get a few opinions at this point. So blast away... or hopefully tell me what you like about it instead. ;-) (Any suggestions or contributions to make it better would be appreciated.) Cheers, Ron Adam """ IMPROVED SLICING ================ Slicing is one of the best features of Python in my opinion, but when you try to use negative index's and or negative step increments it can be tricky and lead to unexpected results. This topic has come up fairly often on comp.lang.python, and often times, the responses include: * Beginners should avoid negative extended slices. * Slices with negative step increments are for advanced python programmers. * It's not broke if you look at it in a different way. * You should do it a different way. All of these and various responses similar to them are unsatisfactory in my opinion and it's a good indicator it's not as good as it could be. Indexing and slice operations are vary common in Python and I don't think we should be discouraging people from learning and using them. COMPATIBILITY ------------- Because the following suggested changes will break current code, it probably can not be implemented prior to Python 3000. + Direct indexing with both positive and negative values returns the same items as they do now. + Extended slices with all positive and or empty default values remain unchanged. - Extended slices with negative values return values that have less items than currently. - Slices with negative step values return entirely different results. REVERSE ORDER STEPPING ---------------------- When negative steps are used, a slice operation does the following. (or the equivalent) 1. reverse the list 2. cut the reversed sequence using start and stop 3. iterate forward using the absolute value of step. * This order results in an inverse selection and I believe should be considered a bug. Changing the order in the following way results in a much more predictable pattern which is both easier to understand and use. 1. cut sequence using start and stop. 2 reverse the order of the results. 3. iterate forward using the absolute value of step. CURRENT INDEXING ---------------- Given a range [a,b,c]: Positive indexing | a | b | c | +---+---+---+ 0 1 2 3 Current negative indexing. | a | b | c | +---+---+---+ -3 -2 -1 -0 When a single index is used the item to the right of the index for both positive and negative index's is returned. With slices, the items between start, and stop index's are returned. Accessing a range at the end of a list numerically becomes inconvenient when negative index's are used as the '-0'th position can not be specified numerically with negative values. ONES BASED NEGATIVE INDEXING ---------------------------- Making negative index's Ones based, and selecting individual item to the left of negative index's would enable addressing the end of the list numerically. Ones based negative index's. | a | b | c | +---+---+---+ -4 -3 -2 -1 Then: a[-1] -> c # item left of index, same result as now. a[-3:-2] -> b # item between index's a[-1:-1] = [d] # insert item at the end. USE OF '~' IN PLACE OF NEGATIVE INDEX'S --------------------------------------- The '~' is the binary not symbol which when used with integers returns the two's compliment. This works nice with indexing from the end of a list because convieniently ~0 == -1. This creates a numerical symmetry between positive indexing and '~' "End of list" indexing. a[0] -> first item in the list. a[~0] -> last item in the list. a[0:~0] -> whole list. a[1:~1] -> center, one position from both ends. * Note: using '~' works as described here in place of single negative index's in current versions of Python. It does not work as described here for extended slices. """ # TEST LIST CLASS. """ A list class to Test '~' end of list indexing. * This class modifies the slice before returning a value. The final implementation may do this by modifying slice objects directly or the underlying C code of sequences. """ class nxlist(object): def __init__(self, value): self.value = value def normslc(self, slc): start,stop,step = slc.start, slc.stop, slc.step if type(start) == int and start<0: start = len(self.value)+start+1 if type(stop) == int and stop<0: stop = len(self.value)+stop+1 return slice(start,stop,step) def __getitem__(self, i): tp = i.__class__ if tp == int: if i>=0: return self.value[i] else: return self.value[ len(self.value)+i ] if tp == slice: slc = self.normslc(i) value = self.value[slc.start:slc.stop] if type(i.step) == int and i.step<0: value.reverse() slc = slice(None,None,-i.step) else: slc = slice(None,None,i.step) return value[slc] #def __setitem__(self, i, v): #Not emplimented yet. def __repr__(self): return 'nxlist(%r)'%self.value a = nxlist(range(10)) print a testdata = [ ('a[0]'), ('a[1]'), ('a[~0]'), ('a[~1]'), ('a[:]'), ('a[:~1]'), ('a[~1:]'), ('a[::]'), ('a[0:~0]'), ('a[1:~1]'), ('a[~1:1]'), ('a[::-2]'), ('a[:3]'), ('a[3:]'), ('a[~3:]'), ('a[:~3]'), ('a[:3:-1]'), ('a[3::-1]'), ('a[~3::-1]'), ('a[:~3:-1]'), ('a[:3:-2]'), ('a[3::-2]'), ('a[~3::-2]'), ('a[:~3:-2]'), ] for n, s in enumerate(testdata): print '%r. %s = %r' % (n,s,eval(s)) """ nxlist([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) 0. a[0] = 0 1. a[1] = 1 2. a[~0] = 9 3. a[~1] = 8 4. a[:] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 5. a[:~1] = [0, 1, 2, 3, 4, 5, 6, 7, 8] 6. a[~1:] = [9] 7. a[::] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 8. a[0:~0] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 9. a[1:~1] = [1, 2, 3, 4, 5, 6, 7, 8] 10. a[~1:1] = [] 11. a[::-2] = [9, 7, 5, 3, 1] 12. a[:3] = [0, 1, 2] 13. a[3:] = [3, 4, 5, 6, 7, 8, 9] 14. a[~3:] = [7, 8, 9] 15. a[:~3] = [0, 1, 2, 3, 4, 5, 6] 16. a[:3:-1] = [2, 1, 0] 17. a[3::-1] = [9, 8, 7, 6, 5, 4, 3] 18. a[~3::-1] = [9, 8, 7] 19. a[:~3:-1] = [6, 5, 4, 3, 2, 1, 0] 20. a[:3:-2] = [2, 0] 21. a[3::-2] = [9, 7, 5, 3] 22. a[~3::-2] = [9, 7] 23. a[:~3:-2] = [6, 4, 2, 0] """ r = range(10) a = nxlist(r) print r[~0],a[~0] # ok print r[~3:],a[~3:] # one off print r[~3::-1],a[~3::-1] # other side print r[~3::-2],a[~3::-2] # other side """ Comparisons of negative indexing and '~' indexing with same values. current, proposed 9 9 [6, 7, 8, 9] [7, 8, 9] [6, 5, 4, 3, 2, 1, 0] [9, 8, 7] [6, 4, 2, 0] [9, 7] """ -- http://mail.python.org/mailman/listinfo/python-list