Перебор списка или чисел по кругу (циклический обход)

Бывает необходимость в переборе списка (в принципе элементов любой структуры) или последовательности чисел таким образом, чтобы достигнув конца (последнего элемента или предела числового ряда) перебор начинался сначала. Другими словами, речь идет о циклическом обходе.

Условие окончания такого перебора зависит от конкретной программы. Так в событийно-ориентированных очередной элемент может запрашиваться только при определенном событии. При этом количество проходов по структуре никак не лимитируется и не влияет на завершение работы приложения. Однако в других случаях и демонстрационных примерах мы должны решить, когда обход должен завершаться.

Способов циклического перебора немало. Выбор оптимального зависит от программы, условия завершения извлечения элементов, список это или числовая последовательность.

Наиболее очевидный способ - это ввести счетчик. Когда он достигает последнего элемента, следует его обнулять:

colors = ['red', 'green', 'blue', 'orange']
i = 0

while True:
    if input() != '':  # любое условие окончания цикла
        break
    print(colors[i])
    i += 1
    if i == len(colors):
        i = 0
for j in range(len(colors)*2):  # любое ограничение числа проходов по списку
    print(colors[i])
    i += 1
    if i == len(colors):
        i = 0
x = 0
offset = 25
limit = 100
count = 0

while count < 3:
    print(x, end=' ')
    x += offset
    if x > limit:
        count += 1
        x = 0

Наиболее элегантным способом такого решения является использование операции нахождение остатка вместо условного оператора:

colors = ['red', 'green', 'blue', 'orange']
i = 0

while True:
    if input() != '': break
    print(colors[i])
    i += 1
    i %= len(colors)
x = 0
offset = 25
limit = 125
count = 0

for i in range(15):
    print(x, end=' ')
    x += offset
    x %= limit

Здесь, когда находится остаток от деления на максимум, переменной заново присваивается то же самое значение за исключением случая, когда она равна максимуму. В этом случае переменная обнуляется.

В языке программирования Python есть модуль itertools, а в нем функция cycle, которая создает бесконечный итератор.

from itertools import cycle

lst = ['a', 'b', 'c', 'd']

for i in cycle(lst):
    if input() != '': break
    print(i)

В cycle можно передать объект range. Например, cycle(range(0, 101, 20)).

Если объект, возвращаемый cycle(), передать в функцию enumerate(), будет возвращен экземпляр класса enumerate. В нем каждому значению будет сопоставлен индекс. Экземпляры enumerate связаны со структурами, от которых были созданы. Поэтому если список конечен, enumerate будет таким же. Но поскольку экземпляр cycle бесконечен, enumerate не имеет последнего элемента.

pool = cycle(lst)
epool = enumerate(pool)

for i, item in epool:
    print(i, item)
    if i == 9: break

Если надо выводить не индекс в enumerate, а индекс элемента в исходном списке, то также можно воспользоваться операцией нахождения остатка: print(i%len(lst), item).

Когда необходимо целое число циклических проходов, можно воспользоваться вложением цикла в цикл:

lst = ['a', 'b', 'c', 'd']

while True:
    for i in lst:
        print(i, end=' ')
    if input() != '': break