# Thoughts on PEP284

Stephen Horne \$\$\$\$\$\$\$\$\$\$\$\$\$\$\$\$\$ at \$\$\$\$\$\$\$\$\$\$\$\$\$\$\$\$\$\$\$\$.co.uk
Wed Sep 24 01:28:44 CEST 2003

```On Tue, 23 Sep 2003 22:03:04 +0100, Stephen Horne
<\$\$\$\$\$\$\$\$\$\$\$\$\$\$\$\$\$@\$\$\$\$\$\$\$\$\$\$\$\$\$\$\$\$\$\$\$\$.co.uk> wrote:

>On Tue, 23 Sep 2003 12:43:34 -0700, Tim Hochberg
><tim.hochberg at ieee.org> wrote:
>
>>I know someone's going to suggest that, since I'm already using slices,
>>I should be in favor of int[a:b:c] or similar. But, that doesn't work
>>any better than range with reversed ranges since you still have to use
>>two slices to make things clear. That is, to get the equivalent of
>>``range(n-1,-1,-1)`` one would need to use ``int[n-1:-1:-1]``, which is
>>no clearer, or ``int[:n][::-1]`` which doesn't seem like a particular
>>win over ``range(n)[::-1]``, which you can do now.

...

>Running with this idea, how about...
>
>  for i in int.backward [:n] :
>    ...

...

>Anyway, I really don't think this 'backward' property should be part
>of the standard int type - I'm rapidly getting more into the idea of
>taking Seans prototype from here...
>
>Message-ID: <lWYbb.4739\$yD1.697869 at news20.bellglobal.com>
>
>removing the inheritance from int (which is probably redundant) and
>making it purely into a library object or recipe. It may be time for
>me to properly figure out metatypes ;-)

Well, I read up on metatypes (a bit of an anticlimax there - two pages
of Python in a Nutshell seemed to cover it) but then of course I
realised that if I'm not making a subtype of int I don't need
metatypes. So here we have noddy implementation 1, which I have saved
as "islice.py"...

#################################################################
#
#  Library allowing slicing into the 'set of integers'.
#
#  The only identifier intended for export is 'xint'.
#
#  Slicing of xint results in an equivalent xrange object. The slicing
#  is handled as follows...
#    start : defaults to zero
#    stop  : if undefined, raise IndexError
#    step  : defaults to one
#
#  object, also used primarily for slicing. When sliced, it behaves
#  exactly as 'xint' *except* that the items in the slice are iterated
#  in reverse order.
#
#  Note - negative start and stop values do not have any special
#  meaning (-1 is not equivalent to the highest integer, for instance)
#  because slices including -ve values may be useful in their own
#  right.
#
#  Example...
#
#    from islice import xint
#
#    for i in xint [:5] :
#      print i, " * 10 is ", i*10
#
#    for i in xint.backward [:5] :
#      print i, " * 10 is ", i*10
#
#  Expected results...
#
#    0 * 10 is 0
#    1 * 10 is 10
#    2 * 10 is 20
#    3 * 10 is 30
#    4 * 10 is 40
#    4 * 10 is 40
#    3 * 10 is 30
#    2 * 10 is 20
#    1 * 10 is 10
#    0 * 10 is 0
#

class Backward (object) :
"""
This class handles reverse-order slicing into the set of integers.
It gives exactly the same slice as AllInts (which is not the same as
simply reversing start and stop and negating step) but iterates the
items in reverse order.

An instance of this class is returned as the result of evaluating
the xint.backward property.
"""

def __getitem__ (self, p) :
if isinstance (p, slice) :
start = p.start or 0
stop  = p.stop
step  = p.step  or 1

if stop  is None :
raise IndexError, "Slice stop cannot be defaulted"

if step  == 0 :
raise IndexError, "Slice step cannot be zero"

count = ((stop - 1) - start) // step

stop   = start + count * step
start -= step

return xrange (stop, start, -step)

else :
raise IndexError, "Non-slice subscripting is not supported"

class AllInts (object) :
"""
This class handles forward-order slicing into the set of integers,
and provides the property "backward" which gives an object that can
be used for reverse-order slicing.

The slice returned is actually an xrange object which iterates the
correct values in the correct order.

This class should not normally be instantiated by users - there is
an instance called 'xint' which should be used instead.
"""

def __getitem__ (self, p) :
if isinstance (p, slice) :
if p.stop  is None :
raise IndexError, "Slice stop cannot be defaulted"

if p.step  == 0 :
raise IndexError, "Slice step cannot be zero"

return xrange (p.start or 0, p.stop, p.step or 1)

else :
raise IndexError, "Non-slice subscripting is not supported"

def getBackward (self) :
return Backward ()

backward = property(getBackward)

xint = AllInts ()

#################################################################

And some quick tests...

Python 2.3c1 (#44, Jul 18 2003, 14:32:36) [MSC v.1200 32 bit (Intel)]
on win32
>>> from islice import xint
>>> list(xint[:10])
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> list(xint.backward[:10])
[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
>>> list(xint.backward[:10:3])
[9, 6, 3, 0]
>>> list(xint.backward[:10:2])
[8, 6, 4, 2, 0]
>>> list(xint.backward[:10:7])
[7, 0]
>>> list(xint.backward[:10:-7])
[]
>>> list(xint.backward[:-10:-7])
[-7, 0]
>>> list(xint[:-10:-7])
[0, -7]

I'm not entirely happy with the names, and maybe a C extension module
might be more efficient, but in principle I'm quite happy with this.

--
Steve Horne

steve at ninereeds dot fsnet dot co dot uk

```