destructuring tuple and list

Jeff Epler jepler at unpythonic.net
Wed Mar 17 10:59:06 EST 2004


This feature has been requested before, and will probably be requested
again.  However, I believe that Guido has stated he doesn't plan to add
it.

In the meantime, it's possible to do this:

>>> from slicer import slicer
>>> s = (3, 4, 5, 6)
>>> x, y, tail = slicer[0,1,...](s)
>>> print x, y, tail
3 4 (5, 6)

I just coded the module up, but I'm confident that it at least passes
its test suite.

I don't know whether
    slicer(s)[0,1,...]
or  
    slicer[0,1....](s)
is the better syntax.  Either is possible, but I happened to write the
first.  This means that you could write
    get_mode_size = slicer[stat.ST_MODE, stat.ST_SIZE]
    for f in files:
        info[f] = get_mode_size(os.stat(f))

Jeff
-------------- next part --------------
__all__ = ['slicer']

import sys

SliceType = type(slice(0))

class Slicer:
    """slicer[x1, x2, ..., xn](seq)
Slice and index seq according to the given xi.  Returns an iterator.

If xi is an iteger or a slice, then the i'th item returned by the
generator is seq[xi].  If it is an ellipsis, then it returns the items
between the end of xi-1 and the start of xi+1.  If x1 is ellipsis, then
the first item is seq[0], and if xn is ellipsis then the last item is
seq[-1].

>>> l = range(10)
>>> l13, l5, rest = slicer[1:3, 5, ...](l)
>>> print l13, l5, rest
[1, 2] 5 [6, 7, 8, 9]

See the body of slicer.test() for more examples."""

    def __getitem__(self, i):
        if isinstance(i, (list, tuple)):
            i = tuple(i)
        else:
            i = (i,)
        return Slices(i)
slicer = Slicer()

def upper_bound(s):
    if isinstance(s, SliceType):
        if s.step is None or s.step > 0:
            return s.stop
        else:
            return s.start+1
    else:
        if s == -1: return sys.maxint
        return s+1

def lower_bound(s):
    if isinstance(s, SliceType):
        if s.step is None or s.step > 0:
            return s.start
        else:
            return s.stop+1
    else:
        return s

class Slices:
    def __init__(self, slices):
        self.slices = slices
    def __call__(self, seq):
        last_upper_bound = 0
        saw_ellipsis = False
        for s in self.slices:
            if s is Ellipsis:
                if saw_ellipsis:
                    raise ValueError, "Cannot have two consecutive Ellipsis"
                saw_ellipsis = True
                continue
            if saw_ellipsis:
                yield seq[last_upper_bound:lower_bound(s)]
                saw_ellipsis = False
            if isinstance(s, SliceType) and s.step is None:
                yield seq[s.start:s.stop]
            else:
                yield seq[s]
            last_upper_bound = upper_bound(s)
        if saw_ellipsis:
                yield seq[last_upper_bound:]

def test():
    l = range(10)

    # Test that ellipsis takes the whole list
    m = list(slicer[...](l))
    print m
    assert m == [l]

    # Test that slicer works right in the middle of the list
    m = list(slicer[0:3, ..., -3:](l))
    print m
    assert m == [[0,1,2], [3,4,5,6], [7,8,9]]

    # Test that slicer gets single items correctly
    m = list(slicer[1, ..., 5](l))
    print m
    assert m == [1, [2,3,4], 5]

    # Test that slicer gets single items (negative indices)
    m = list(slicer[-3, ..., -1](l))
    print m
    assert m == [7, [8], 9]

    # Test that slicer gets stepped slices right
    m = list(slicer[0:5:2, ...](l))
    print m
    assert m == [[0,2,4], [5,6,7,8,9]]

    # Test that slicer gets negative stepped slices right
    m = list(slicer[5:0:-2, ...](l))
    print m
    assert m == [[5,3,1], [6,7,8,9]]

    # Test that slicer gets negative stepped slices right (part 2)
    m = list(slicer[..., 9:6:-2](l))
    print m
    assert m == [[0,1,2,3,4,5,6], [9,7]]

    # Test that slicer gets empty ... right
    m = list(slicer[9, ..., 0](l))
    print m
    assert m == [9, [], 0]

    # Test that slicer gets empty ... right (slice version)
    m = list(slicer[0:5, ..., 5:10](l))
    print m
    assert m == [[0,1,2,3,4], [], [5,6,7,8,9]]

if __name__ == '__main__': test()


More information about the Python-list mailing list