2013-03-15 2 views
4

Скажем, у меня есть массив foo, например. элементы [1, 2, 3], и что я хочу извлечь элементы из foo, как если бы foo был «бесконечно конкатенированный».Циклическое индексирование списков в Python

Например foo[0:2] вернется (как обычный список):

[1, 2]

и foo[0:5] вернутся:

[1, 2, 3, 1, 2] 

в то время как foo[7:13] вернется:

[2, 3, 1, 2, 3, 1] 

Существуют ли какие-либо контейнеры данных в Python или расширенные модули, которые уже облегчают этот тип доступа? Если нет, то каким будет хороший/простой способ предоставить этот контейнер?

ответ

11

Боюсь, вам придется реализовать его самостоятельно. Это не сложно, хотя:

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] 

Это отсутствуют некоторые детали, как заботиться о опущенных параметров среза, отрицательных чисел в срезах, и шаг среза.

2

Вы должны быть осторожны при работе с последовательностями, которые выглядят как списки, но ведут себя по-разному. Я бы предложил использовать прохладную реализацию Pavel Anossov, но предоставил назначенные get_cyclic_item и get_cyclic_slice, вместо __getitem__ и __getslice__.

Пользователь класса может легко сделать предположения о поведении списка, который он использует (ожидая отношения ISA, как в «списке циклических IS-списков»), что приведет к ошибкам/ошибкам.

Вот некоторые примеры случаев, когда использование списка может привести к путанице, если вызывающий абонент не знает, что он с помощью cyclicallist вместо обычного списка ...

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] 

И, конечно же, семантическая для пустой cyclicallist не очень хорошо определен ...

+2

Я не знаю контекста OP, но большинство из этих проблем, по-видимому, разрешимо, определяя новую структуру данных как неизменный тип, и этого может быть достаточно для него/нее. Не то чтобы вы ошибаетесь - я тоже буду использовать ваш подход! - но интересно, что такое ограничение может решить некоторые проблемы и, если OP действительно нуждается в этом поведении, вероятно, ограничение также действует в проблеме OP. – brandizzi

+2

@brandizzi, хороший пункт. Возможно, «class cyclicaltuple (tuple): ...» - это путь. – shx2

+0

Кортеж был бы семантически неправильным. Кортеж - это запись с фиксированным и известным количеством элементов. Также вы не можете назвать это 'cyclist' :) Я думаю, что достаточно произнести' cyclist 'immutable (возможно, наследуется от' collections.Sequence'). –

2

Даже если это будет смешно неэффективным по сравнению с реализацией на основе предложенной выше по модулю, я думаю, что использование itertools может быть интересным способом сделать это ...

>>> 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] 
Смежные вопросы