On 29 October 2013 20:23, Paul Moore <p.f.moore@gmail.com> wrote:
On 28 October 2013 22:41, Guido van Rossum <guido@python.org> wrote:
I'm not sure I like new syntax. We'd still have to find a way to represent this with slice() and also with range().
It's a shame there isn't an indexing syntax where you can supply an iterator that produces the set of indexes you want and returns the subsequence - then we could experiment with alternative semantics in user code.
So, for example (silly example, because I don't have the time right now to define an indexing function that matches any of the proposed solutions):
>>> def PrimeSlice(): >>> yield 2 >>> yield 3 >>> yield 5 >>> yield 7
>>> 'abcdefgh'[[PrimeSlice()]] 'bceg'
But of course, to make this user-definable needs new syntax in the first place :-(
Tangent: I thought of a list comprehension based syntax for that a while ago, but decided it wasn't particularly interesting since it's too hard to provide sensible fallback behaviour for existing containers: 'abcdefgh'[x for x in PrimeSlice()] Back on the topic of slicing with negative steps, I did some experimentation to see what could be done in current Python using a callable that produces the appropriate slice objects, and it turns out you can create a quite usable "rslice" callable, provided you pass in the length when dealing with mismatched signs on the indices (that's the advantage of the "[i:j][::-k]" interpretation of the reversed slice - if you want to interpret it as "[i:j:k][::-1]" as I suggested previously, I believe you would need to know the length of the sequence in all cases): def rslice(*slice_args, length=None): """For args (i, j, k) computes a slice equivalent to [i:j][::-k] (which is not the same as [i:j:-k]!)""" forward = slice(*slice_args) # Easiest way to emulate slice arg parsing! # Always negate the step step = -forward.step # Given slice args are closed on the left, open on the right, # simply negating the step and swapping left and right will introduce # an off-by-one error, so we need to adjust the endpoints to account # for the open/closed change left = forward.start right = forward.stop # Check for an empty slice before tinkering with offsets if left is not None and right is not None: if (left >= 0) != (right >= 0): if length is None: raise ValueError("Must supply length for indices of different signs") if left < 0: left += length else: right += length if left >= right: return slice(0, 0, 1) stop = left if stop is not None: # Closed on the left -> open stop value in the reversed slice if stop: stop -= 1 else: # Converting a start offset of 0 to an end offset of -1 does # the wrong thing - need to convert it to None instead stop = None start = right if start is not None: # Open on the right -> closed start value in the reversed slice if start: start -= 1 else: # Converting a stop offset of 0 to a start offset of -1 does # the wrong thing - need to convert it to None instead start = None return slice(start, stop, step) # Test case data = range(10) for i in range(-10, 11): for j in range(-10, 11): for k in range(1, 11): expected = data[i:j][::-k] actual = data[rslice(i, j, k, length=len(data))] if actual != expected: print((i, j, k), actual, expected) So, at this point, I still quite like the idea of adding a "reverse=True" keyword only arg to slice and range (with the semantics of rslice above), and then revisit the idea of offering syntax for it in Python 3.5. Since slices are objects, they could store the "reverse" flag internally, and only apply it when the indices() method (or the C API equivalent) is called to convert the abstract indices to real ones for the cases where the info is needed - otherwise they'd do the calculation above to create a suitable "forward" definition for maximum compatibility with existing container implementations. A separate keyword only arg like "addlen=True" would also make it possible to turn off the negative indexing support in a slice object's indices() method, and switch it to clamping to zero instead. An alternative to both of those ideas would be to eliminate the restriction on subclassing slice objects in CPython, then you could implement slice objects with different indices() method behaviour (they'd still have to produce a start, stop, step triple though, so they wouldn't offer the full generality Paul was describing). Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia