Reputation: 96350
Say I have an array foo
with e.g. elements [1, 2, 3]
, and that I want to retrieve elements of foo
as if foo
had been "infinitely concatenated".
For example foo[0:2]
would return (like a normal list):
[1, 2]
and foo[0:5]
would return:
[1, 2, 3, 1, 2]
while foo[7:13]
would return:
[2, 3, 1, 2, 3, 1]
Are there any data containers in Python or extended modules that already facilitate this type of access? If not, what would be a good/easy way to provide this container?
Upvotes: 4
Views: 444
Reputation:
Even if it will be ridiculously inefficient compared to the modulo based implementation suggested above, I think that using itertools
could be a fun way to do it…
>>> from itertools import islice, cycle
>>> make_cyclic = lambda lst: lambda start, stop: list( islice( cycle( lst ), start, stop ) )
>>> make_cyclic( [ 1, 2, 3 ] )
>>> c(7, 13)
[2, 3, 1, 2, 3, 1]
Upvotes: 2
Reputation: 62928
I'm afraid you'll have to implement it yourself. It isn't difficult though:
class cyclist(list):
def __getitem__(self, index):
return list.__getitem__(self, index % len(self))
def __getslice__(self, start, stop):
return [self[n] for n in range(start, stop)]
foo = cyclist([1, 2, 3])
print foo[0:2] # [1, 2]
print foo[7:13] # [2, 3, 1, 2, 3, 1]
print foo[0:5] # [1, 2, 3, 1, 2]
It's missing some details like taking care of omitted slice parameters, negative numbers in slices, and slice step.
Upvotes: 11
Reputation: 64328
You should be careful when working with sequences which look like lists but behave inherently different.
I would suggest using Pavel Anossov's cool implementation, but providing designated get_cyclic_item
and
get_cyclic_slice
, instead of overriding list's __getitem__
and __getslice__
.
The user of the class can easily make assumptions about the behavior of the list he's using (expecting an ISA relation, as in "cyclicallist IS A list"), which would lead to bugs/errors.
Here are some examples for cases where using your list can get confusing, if the caller is not aware he's using a cyclicallist
instead of a regular list...
a = cyclicallist([ 0, 1, 2 ])
# appending a value at the end changes an "existing" index
print a[100]
a.append(99)
print a[100]
# deleting a value changes an index preceding it
print a[100]
del a[999] # currently gives an error: IndexError: list assignment index out of range
print a[100] # even if no error, what should this print?
# hmm...
del a[100:99999]
And of course, the semantic for empty cyclicallist
is not well-defined...
Upvotes: 2