[Python-ideas] Possible new slice behaviour? Was ( Negative slice discussion.)

Nick Coghlan ncoghlan at gmail.com
Mon Nov 4 23:40:37 CET 2013


On 5 Nov 2013 03:35, "Ron Adam" <ron3200 at gmail.com> wrote:
>
>
> 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.
>
> This matches the slice semantics that Guido likes.
> (possibly for python 4.)
>
> The ability to pass callables along with the slice creates an easy and
clean way to add new indexing modes.  (As Nick suggested.)

Tuples can't really be used for this purpose, since that's incompatible
with multi-dimensional indexing.

However, I also agree containment would be a better way to go than
subclassing.

I'm currently thinking that a fourth "adjust" argument to the slice
constructor may work, and call that from the indices method as:

  def adjust_indices(start, stop, step, length):
   ...

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.

The result would be used as the result of the indices method.

Cheers,
Nick.

>
> Overall, this makes everything simpler and easier to do.  :-)
>
>
> Cheers,
>     Ron
>
>
> """
>
>     An improved slice implementation.
>
>     Even the C source says:
>
>         "It's harder to get right than you might think."
>
>  both in code and in understanding
>     This requires changing slice indexing so that
>     the following relationships are true.
>
>        s[i:j:k]   ==  s[i:j][k] both in code and in understanding
>
>        s[i:j:-1]  ==  s[i:j:1][::-1]
>
>
>     And it also adds the ability to apply callables to
>     slices and index's using the existing slice syntax.
>
>     These alterations would need to be made to the __getitem__
>     and __setitem__, methods of built-in types.  Possibly in
>     Python 4.0.
>
>     *(I was not able to get a clean version of this behaviour
>     with the existing slice semantics.  But the slice and index
>     behaviour of this implementation is much simpler and makes
>     using callables to adjust index's very easy.  That seems
>     like a good indication that changing slices to match the
>     above relationships is worth doing.
>
>     It may be possible to get the current behaviour by applying
>     a callable to the slice like the open, closed, and ones index
>     examples below.
>     )
>
> """
>
> # A string sub-class for testing.
>
> class Str(str):
>
>     def _fix_slice_indexes(self, slc):
>         #  Replace Nones and check step value.
>         if isinstance(slc, int):
>             return slc
>         i, j, k = slc.start, slc.stop, slc.step
>         if k == 0:
>             raise ValueError("slice step cannot be zero")
>         if i == None: i = 0
>         if j == None: j = len(self)
>         if k == None: k = 1
>         return slice(i, j, k)
>
>     def __getitem__(self, args):
>         """
>         Gets a item from a string with either an
>         index, or slice.  Apply any callables to the
>         slice if they are pressent.
>
>         Valid inputes...
>              i
>              (i, callables ...)
>              slice()
>              (slice(), callables ...)
>
>         """
>         # Apply callables if any.
>         if isinstance(args, tuple):
>             slc, *callables = args
>             slc = self._fix_slice_indexes(slc)
>             for fn in callables:
>                 slc = fn(self, slc)
>         else:
>             slc = self._fix_slice_indexes(args)
>
>         # Just an index
>         if isinstance(slc, int):
>             return str.__getitem__(self, slc)
>
>         # Handle slice.
>         rval = []
>         i, j, k = slc.start, slc.stop, slc.step
>         ix = i if k > 0 else j-1
>         while i <= ix < j:
>             rval.append(str.__getitem__(self, ix))
>             ix += k
>         return type(self)('').join(rval)
>
>
> """
>    These end with 'i' to indicate they make index adjustments,
>    and also to make them less likely to clash with other
>    functions.
>
>    Some of these are so simple, you'd probably just
>    adjust the index directly, ie.. reversei.  But
>    they make good examples of what is possible.  And possible
>    There are other uses as well.
>
>    Because they are just objects passed in, the names aren't
>    important. They can be called anything and still work, and
>    the programmer is free to create new alternatives.
> """
>
> def reversei(obj, slc):
>     """Return a new slice with reversed step."""
>     if isinstance(slc, slice):
>         i, j, k = slc.start, slc.stop, slc.step
>         return slice(i, j, -k)
>     return slc
>
> def trimi(obj, slc):
>     """Trim left and right so an IndexError is not produced."""
>     if isinstance(slc, slice):
>         ln = len(obj)
>         i, j, k = slc.start, slc.stop, slc.step
>         if i<0: i = 0
>         if j>ln: j = ln
>         return slice(i, j, k)
>     return slc
>
> def openi(obj, slc):
>     """Open interval - Does not include end points."""
>     if isinstance(slc, slice):
>         i, j, k = slc.start, slc.stop, slc.step
>         return slice(i+1, j, k)
>     return slc
>
> def closedi(obj, slc):
>     """Closed interval - Includes end points."""
>     if isinstance(slc, slice):
>         i, j, k = slc.start, slc.stop, slc.step
>         return slice(i, j+1, k)
>     return slc
>
> def onei(obj, slc):
>     """First element is 1 instead of zero."""
>     if isinstance(slc, slice):
>         i, j, k = slc.start, slc.stop, slc.step
>         return slice(i-1, j-1, k)
>     return slc - 1
>
>
>
> def _test_cases1():
>     """
>
>     # test string
>     >>> s = Str('0123456789')
>
>     #  |0|1|2|3|4|5|6|7|8|9|
>     #  0 1 2 3 4 5 6 7 8 9 10
>     # 10 9 8 7 6 5 4 3 2 1 0
>
>
>     >>> s[:]
>     '0123456789'
>
>     >>> s[:, trimi]
>     '0123456789'
>
>     >>> s[:, reversei]
>     '9876543210'
>
>     >>> s[:, reversei, trimi]
>     '9876543210'
>
>     >>> s[::, trimi, reversei]
>     '9876543210'
>
>
>     # Right side bigger than len(s)
>
>     >>> s[:100]
>     Traceback (most recent call last):
>     IndexError: string index out of range
>
>     >>> s[:100, trimi]
>     '0123456789'
>
>     >>> s[:100, trimi, reversei]
>     '9876543210'
>
>     >>> s[:100, reversei]
>     Traceback (most recent call last):
>     IndexError: string index out of range
>
>     >>> s[:100, reversei, trimi]
>     '9876543210'
>
>
>     # Left side smaller than 0.
>
>     >>> s[-100:]
>     Traceback (most recent call last):
>     IndexError: string index out of range
>
>     >>> s[-100:, trimi]
>     '0123456789'
>
>     >>> s[-100:, trimi, reversei]
>     '9876543210'
>
>
>     # Slice bigger than s.
>
>     >>> s[-100:100]
>     Traceback (most recent call last):
>     IndexError: string index out of range
>
>     >>> s[-100:100, trimi]
>     '0123456789'
>
>
>     # Slice smaller than s.
>
>     >>> s[3:7]
>     '3456'
>
>     >>> s[3:7, reversei]
>     '6543'
>
>
>
>     # From left With negative step.
>
>     >>> s[::-1]
>     '9876543210'
>
>     >>> s[::-1, reversei]
>     '0123456789'
>
>     >>> s[:100:-1, trimi]      # j past right side
>     '9876543210'
>
>     >>> s[-100::-1, trimi]     # i before left side
>     '9876543210'
>
>     >>> s[-100:100:-1, trimi, reversei]    # slice is bigger
>     '0123456789'
>
>
>
>     # Null results
>
>     >>> s[7:3:1, trimi]
>     ''
>
>     >>> s[7:3:-1, trimi]
>     ''
>
>
>     # Check None values.
>
>     >>> s[:]
>     '0123456789'
>
>     >>> s[None:None]
>     '0123456789'
>
>     >>> s[None:None:None]
>     '0123456789'
>
>     >>> s[:: 1]
>     '0123456789'
>
>     >>> s[::-1]
>     '9876543210'
>
>     >>> s[None:None:1]
>     '0123456789'
>
>     >>> s[None:None:-1]
>     '9876543210'
>
>
>     # Check error messages.
>
>     >>> s[0:0:0:0]
>     Traceback (most recent call last):
>     SyntaxError: invalid syntax
>
>     >>> s[5:5:0]
>     Traceback (most recent call last):
>     ValueError: slice step cannot be zero
>
>
>
>     # And various other combinations.
>
>     >>> s = Str('123456789')
>
>     >>> s[3, onei]
>     '3'
>
>     >>> s[4:8, onei]
>     '4567'
>
>     >>> s[4:8, onei, openi]
>     '567'
>
>     >>> s[4:8, onei, closedi]
>     '45678'
>
>
>     """
>
>
> def _test():
>     import doctest
>     print(doctest.testmod(verbose=False))
>
>
> if __name__=="__main__":
>     _test()
>
>
> _______________________________________________
> Python-ideas mailing list
> Python-ideas at python.org
> https://mail.python.org/mailman/listinfo/python-ideas
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/python-ideas/attachments/20131105/1888112a/attachment-0001.html>


More information about the Python-ideas mailing list