Циклический просмотр списка, начиная с определенного элемента

Скажем, у меня есть список:

l = [1, 2, 3, 4]

И я хочу пройти через это. Обычно он делал бы что-то вроде этого,

1, 2, 3, 4, 1, 2, 3, 4, 1, 2...

Я хочу иметь возможность начать с определенной точки цикла, не обязательно индекса, но, возможно, совпадающего с элементом. Скажем, я хотел начать с любого элемента в списке ==4, тогда вывод будет таким:

4, 1, 2, 3, 4, 1, 2, 3, 4, 1...

Как я могу это сделать?


person john    schedule 20.01.2012    source источник


Ответы (7)


Посмотрите на модуль itertools. Он обеспечивает весь необходимый функционал.

from itertools import cycle, islice, dropwhile

L = [1, 2, 3, 4]

cycled = cycle(L)  # cycle thorugh the list 'L'
skipped = dropwhile(lambda x: x != 4, cycled)  # drop the values until x==4
sliced = islice(skipped, None, 10)  # take the first 10 values

result = list(sliced)  # create a list from iterator
print(result)

Выход:

[4, 1, 2, 3, 4, 1, 2, 3, 4, 1]
person ovgolovin    schedule 20.01.2012
comment
dropwhile здесь не требуется. Вместо этого вы можете использовать list(islice(cycled, L.index(4), L.index(4) + 9)) - person busybear; 27.01.2021

Используйте арифметический оператор mod. Предположим, вы начинаете с позиции k, тогда k следует обновить следующим образом:

k = (k + 1) % len(l)

Если вы хотите начать с определенного элемента, а не с индекса, вы всегда можете найти его, например, k = l.index(x), где x — нужный элемент.

person Sufian Latif    schedule 20.01.2012

Я не такой большой поклонник импорта модулей, когда вы можете сделать что-то самостоятельно в пару строк. Вот мое решение без импорта:

def cycle(my_list, start_at=None):
    start_at = 0 if start_at is None else my_list.index(start_at)
    while True:
        yield my_list[start_at]
        start_at = (start_at + 1) % len(my_list)

Это вернет (бесконечный) итератор, зацикливающий ваш список. Чтобы получить следующий элемент цикла, вы должны использовать оператор next:

>>> it1 = cycle([101,102,103,104])
>>> next(it1), next(it1), next(it1), next(it1), next(it1)
(101, 102, 103, 104, 101) # and so on ...
>>> it1 = cycle([101,102,103,104], start_at=103)
>>> next(it1), next(it1), next(it1), next(it1), next(it1)
(103, 104, 101, 102, 103) # and so on ...
person juliomalegria    schedule 20.01.2012
comment
itertools записывается в C. Таким образом, это довольно быстро, если не считать его красноречия. - person ovgolovin; 20.01.2012
comment
Мне больше всего нравится этот ответ. - person stephanmg; 10.10.2019

import itertools as it
l = [1, 2, 3, 4]
list(it.islice(it.dropwhile(lambda x: x != 4, it.cycle(l)),  10))
# returns: [4, 1, 2, 3, 4, 1, 2, 3, 4, 1]

поэтому итератор, который вам нужен:

it.dropwhile(lambda x: x != 4, it.cycle(l))
person eumiro    schedule 20.01.2012
comment
@gnibbler Потребуется поставить 4 в скобках (4).__cmp__. В противном случае это не работает (по крайней мере, в Python 2.7.2). А со скобками это выглядит не так красиво. - person ovgolovin; 20.01.2012
comment
@gnibbler А в Python 3 потребуется использовать __eq__ вместо __cmp__ (в версии 3 нет __cmp__). - person ovgolovin; 20.01.2012
comment
@ovgolovin То, что написал @gnibbler, работает без скобок. Вы пропустили пробел между 4 и .? - person Duncan; 20.01.2012
comment
@ovgolovin для Python 3 это должно быть __ne__ или __gt__, а не __eq__. - person Duncan; 20.01.2012
comment
пробел между 4 и . ? что, черт возьми, там происходит? - person wim; 20.01.2012
comment
@wim, если вы напишете 4.__cmp__, Python проанализирует его как число с плавающей запятой 4., за которым следует идентификатор __cmp__, и это синтаксическая ошибка. 4 .__cmp__, с другой стороны, представляет собой целое число 4, за которым следует точка, чтобы указать ссылку на атрибут и атрибут __cmp__. - person Duncan; 20.01.2012

Хм, http://docs.python.org/library/itertools.html#itertools.cycle не имеет такого начального элемента.

Может быть, вы все равно запускаете цикл и отбрасываете первые элементы, которые вам не нравятся.

person Community    schedule 20.01.2012

Еще одна странная опция заключается в том, что циклический просмотр списков может выполняться назад. Например:

# Run this once
myList = ['foo', 'bar', 'baz', 'boom']
myItem = 'baz'

# Run this repeatedly to cycle through the list
if myItem in myList:
    myItem = myList[myList.index(myItem)-1]
    print myItem
person Daniel Klug    schedule 13.03.2015

Можно использовать что-то вроде этого:

def my_cycle(data, start=None):
  k = 0 if not start else start
  while True:
    yield data[k]
    k = (k + 1) % len(data)

Затем запустите:

for val in my_cycle([0,1,2,3], 2):
  print(val)

По сути то же самое, что и один из предыдущих ответов. Виноват.

person stephanmg    schedule 10.10.2019