<p dir="ltr"><br>
On 5 Nov 2013 03:35, "Ron Adam" <<a href="mailto:ron3200@gmail.com">ron3200@gmail.com</a>> wrote:<br>
><br>
><br>
> This is one solution to what we can do to make slices both easier to understand and work in a much more consistent and flexible way.<br>
><br>
> This matches the slice semantics that Guido likes.<br>
> (possibly for python 4.)<br>
><br>
> The ability to pass callables along with the slice creates an easy and clean way to add new indexing modes.  (As Nick suggested.)</p>
<p dir="ltr">Tuples can't really be used for this purpose, since that's incompatible with multi-dimensional indexing.</p>
<p dir="ltr">However, I also agree containment would be a better way to go than subclassing.</p>
<p dir="ltr">I'm currently thinking that a fourth "adjust" argument to the slice constructor may work, and call that from the indices method as:</p>
<p dir="ltr">  def adjust_indices(start, stop, step, length):<br>
   ...</p>
<p dir="ltr">The values passed in would be those from the slice constructor plus the length passed to the indices method. The only preconditioning would be the check for a non-zero step. </p>
<p dir="ltr">The result would be used as the result of the indices method.</p>
<p dir="ltr">Cheers,<br>
Nick.</p>
<p dir="ltr">><br>
> Overall, this makes everything simpler and easier to do.  :-)<br>
><br>
><br>
> Cheers,<br>
>     Ron<br>
><br>
><br>
> """<br>
><br>
>     An improved slice implementation.<br>
><br>
>     Even the C source says:<br>
><br>
>         "It's harder to get right than you might think."<br>
><br>
>  both in code and in understanding<br>
>     This requires changing slice indexing so that<br>
>     the following relationships are true.<br>
><br>
>        s[i:j:k]   ==  s[i:j][k] both in code and in understanding<br>
><br>
>        s[i:j:-1]  ==  s[i:j:1][::-1]<br>
><br>
><br>
>     And it also adds the ability to apply callables to<br>
>     slices and index's using the existing slice syntax.<br>
><br>
>     These alterations would need to be made to the __getitem__<br>
>     and __setitem__, methods of built-in types.  Possibly in<br>
>     Python 4.0.<br>
><br>
>     *(I was not able to get a clean version of this behaviour<br>
>     with the existing slice semantics.  But the slice and index<br>
>     behaviour of this implementation is much simpler and makes<br>
>     using callables to adjust index's very easy.  That seems<br>
>     like a good indication that changing slices to match the<br>
>     above relationships is worth doing.<br>
><br>
>     It may be possible to get the current behaviour by applying<br>
>     a callable to the slice like the open, closed, and ones index<br>
>     examples below.<br>
>     )<br>
><br>
> """<br>
><br>
> # A string sub-class for testing.<br>
><br>
> class Str(str):<br>
><br>
>     def _fix_slice_indexes(self, slc):<br>
>         #  Replace Nones and check step value.<br>
>         if isinstance(slc, int):<br>
>             return slc<br>
>         i, j, k = slc.start, slc.stop, slc.step<br>
>         if k == 0:<br>
>             raise ValueError("slice step cannot be zero")<br>
>         if i == None: i = 0<br>
>         if j == None: j = len(self)<br>
>         if k == None: k = 1<br>
>         return slice(i, j, k)<br>
><br>
>     def __getitem__(self, args):<br>
>         """<br>
>         Gets a item from a string with either an<br>
>         index, or slice.  Apply any callables to the<br>
>         slice if they are pressent.<br>
><br>
>         Valid inputes...<br>
>              i<br>
>              (i, callables ...)<br>
>              slice()<br>
>              (slice(), callables ...)<br>
><br>
>         """<br>
>         # Apply callables if any.<br>
>         if isinstance(args, tuple):<br>
>             slc, *callables = args<br>
>             slc = self._fix_slice_indexes(slc)<br>
>             for fn in callables:<br>
>                 slc = fn(self, slc)<br>
>         else:<br>
>             slc = self._fix_slice_indexes(args)<br>
><br>
>         # Just an index<br>
>         if isinstance(slc, int):<br>
>             return str.__getitem__(self, slc)<br>
><br>
>         # Handle slice.<br>
>         rval = []<br>
>         i, j, k = slc.start, slc.stop, slc.step<br>
>         ix = i if k > 0 else j-1<br>
>         while i <= ix < j:<br>
>             rval.append(str.__getitem__(self, ix))<br>
>             ix += k<br>
>         return type(self)('').join(rval)<br>
><br>
><br>
> """<br>
>    These end with 'i' to indicate they make index adjustments,<br>
>    and also to make them less likely to clash with other<br>
>    functions.<br>
><br>
>    Some of these are so simple, you'd probably just<br>
>    adjust the index directly, ie.. reversei.  But<br>
>    they make good examples of what is possible.  And possible<br>
>    There are other uses as well.<br>
><br>
>    Because they are just objects passed in, the names aren't<br>
>    important. They can be called anything and still work, and<br>
>    the programmer is free to create new alternatives.<br>
> """<br>
><br>
> def reversei(obj, slc):<br>
>     """Return a new slice with reversed step."""<br>
>     if isinstance(slc, slice):<br>
>         i, j, k = slc.start, slc.stop, slc.step<br>
>         return slice(i, j, -k)<br>
>     return slc<br>
><br>
> def trimi(obj, slc):<br>
>     """Trim left and right so an IndexError is not produced."""<br>
>     if isinstance(slc, slice):<br>
>         ln = len(obj)<br>
>         i, j, k = slc.start, slc.stop, slc.step<br>
>         if i<0: i = 0<br>
>         if j>ln: j = ln<br>
>         return slice(i, j, k)<br>
>     return slc<br>
><br>
> def openi(obj, slc):<br>
>     """Open interval - Does not include end points."""<br>
>     if isinstance(slc, slice):<br>
>         i, j, k = slc.start, slc.stop, slc.step<br>
>         return slice(i+1, j, k)<br>
>     return slc<br>
><br>
> def closedi(obj, slc):<br>
>     """Closed interval - Includes end points."""<br>
>     if isinstance(slc, slice):<br>
>         i, j, k = slc.start, slc.stop, slc.step<br>
>         return slice(i, j+1, k)<br>
>     return slc<br>
><br>
> def onei(obj, slc):<br>
>     """First element is 1 instead of zero."""<br>
>     if isinstance(slc, slice):<br>
>         i, j, k = slc.start, slc.stop, slc.step<br>
>         return slice(i-1, j-1, k)<br>
>     return slc - 1<br>
><br>
><br>
><br>
> def _test_cases1():<br>
>     """<br>
><br>
>     # test string<br>
>     >>> s = Str('0123456789')<br>
><br>
>     #  |0|1|2|3|4|5|6|7|8|9|<br>
>     #  0 1 2 3 4 5 6 7 8 9 10<br>
>     # 10 9 8 7 6 5 4 3 2 1 0<br>
><br>
><br>
>     >>> s[:]<br>
>     '0123456789'<br>
><br>
>     >>> s[:, trimi]<br>
>     '0123456789'<br>
><br>
>     >>> s[:, reversei]<br>
>     '9876543210'<br>
><br>
>     >>> s[:, reversei, trimi]<br>
>     '9876543210'<br>
><br>
>     >>> s[::, trimi, reversei]<br>
>     '9876543210'<br>
><br>
><br>
>     # Right side bigger than len(s)<br>
><br>
>     >>> s[:100]<br>
>     Traceback (most recent call last):<br>
>     IndexError: string index out of range<br>
><br>
>     >>> s[:100, trimi]<br>
>     '0123456789'<br>
><br>
>     >>> s[:100, trimi, reversei]<br>
>     '9876543210'<br>
><br>
>     >>> s[:100, reversei]<br>
>     Traceback (most recent call last):<br>
>     IndexError: string index out of range<br>
><br>
>     >>> s[:100, reversei, trimi]<br>
>     '9876543210'<br>
><br>
><br>
>     # Left side smaller than 0.<br>
><br>
>     >>> s[-100:]<br>
>     Traceback (most recent call last):<br>
>     IndexError: string index out of range<br>
><br>
>     >>> s[-100:, trimi]<br>
>     '0123456789'<br>
><br>
>     >>> s[-100:, trimi, reversei]<br>
>     '9876543210'<br>
><br>
><br>
>     # Slice bigger than s.<br>
><br>
>     >>> s[-100:100]<br>
>     Traceback (most recent call last):<br>
>     IndexError: string index out of range<br>
><br>
>     >>> s[-100:100, trimi]<br>
>     '0123456789'<br>
><br>
><br>
>     # Slice smaller than s.<br>
><br>
>     >>> s[3:7]<br>
>     '3456'<br>
><br>
>     >>> s[3:7, reversei]<br>
>     '6543'<br>
><br>
><br>
><br>
>     # From left With negative step.<br>
><br>
>     >>> s[::-1]<br>
>     '9876543210'<br>
><br>
>     >>> s[::-1, reversei]<br>
>     '0123456789'<br>
><br>
>     >>> s[:100:-1, trimi]      # j past right side<br>
>     '9876543210'<br>
><br>
>     >>> s[-100::-1, trimi]     # i before left side<br>
>     '9876543210'<br>
><br>
>     >>> s[-100:100:-1, trimi, reversei]    # slice is bigger<br>
>     '0123456789'<br>
><br>
><br>
><br>
>     # Null results<br>
><br>
>     >>> s[7:3:1, trimi]<br>
>     ''<br>
><br>
>     >>> s[7:3:-1, trimi]<br>
>     ''<br>
><br>
><br>
>     # Check None values.<br>
><br>
>     >>> s[:]<br>
>     '0123456789'<br>
><br>
>     >>> s[None:None]<br>
>     '0123456789'<br>
><br>
>     >>> s[None:None:None]<br>
>     '0123456789'<br>
><br>
>     >>> s[:: 1]<br>
>     '0123456789'<br>
><br>
>     >>> s[::-1]<br>
>     '9876543210'<br>
><br>
>     >>> s[None:None:1]<br>
>     '0123456789'<br>
><br>
>     >>> s[None:None:-1]<br>
>     '9876543210'<br>
><br>
><br>
>     # Check error messages.<br>
><br>
>     >>> s[0:0:0:0]<br>
>     Traceback (most recent call last):<br>
>     SyntaxError: invalid syntax<br>
><br>
>     >>> s[5:5:0]<br>
>     Traceback (most recent call last):<br>
>     ValueError: slice step cannot be zero<br>
><br>
><br>
><br>
>     # And various other combinations.<br>
><br>
>     >>> s = Str('123456789')<br>
><br>
>     >>> s[3, onei]<br>
>     '3'<br>
><br>
>     >>> s[4:8, onei]<br>
>     '4567'<br>
><br>
>     >>> s[4:8, onei, openi]<br>
>     '567'<br>
><br>
>     >>> s[4:8, onei, closedi]<br>
>     '45678'<br>
><br>
><br>
>     """<br>
><br>
><br>
> def _test():<br>
>     import doctest<br>
>     print(doctest.testmod(verbose=False))<br>
><br>
><br>
> if __name__=="__main__":<br>
>     _test()<br>
><br>
><br>
> _______________________________________________<br>
> Python-ideas mailing list<br>
> <a href="mailto:Python-ideas@python.org">Python-ideas@python.org</a><br>
> <a href="https://mail.python.org/mailman/listinfo/python-ideas">https://mail.python.org/mailman/listinfo/python-ideas</a><br>
</p>