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