Ітератор кругового списку в Python


99

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

Випадок використання - пул з'єднань. Клієнт запитує про з'єднання, ітератор перевіряє, чи є вказівка ​​на з'єднання, і повертає його, інакше циклічно, поки не знайде доступне.

Чи є акуратний спосіб зробити це в Python?

Відповіді:


159

Використовуйте itertools.cycle, саме його ціль:

from itertools import cycle

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

pool = cycle(lst)

for item in pool:
    print item,

Вихід:

a b c a b c ...

(Очевидно, петлі назавжди)


Щоб вручну просувати ітератор і витягувати з нього значення по черзі, просто зателефонуйте next(pool):

>>> next(pool)
'a'
>>> next(pool)
'b'

1
Ви друкуєте елементи в циклі. Що я хочу покинути цикл і повернутися пізніше? (Я хочу почати там, де я зупинився).
user443854

7
@ user443854 використовуйте, pool.next()щоб отримати єдиний наступний елемент із циклу
Якоб Кралл

4
@ user443854 FWIW це набагато краща відповідь, ніж моя. Немає підстав для повторної реалізації функцій бібліотеки!
Якоб Кралл

5
pool.next () не працював для мене, лише наступний (пул). Можливо, через Python 3?
fjsj

6
@fjsj це правильно, на Python 3 вам потрібно використовувати next(iterator)(який BTW також добре працює на Python 2.x, і тому є канонічною формою, яку слід використовувати). Див. Чи видно generator.next () у python 3.0? для більш поглибленого пояснення. Відповідно оновив мою відповідь.
Лукас Граф

54

Правильна відповідь - використовувати itertools.cycle . Але припустимо, що функція бібліотеки не існує. Як би ти це реалізував?

Використовуйте генератор :

def circular():
    while True:
        for connection in ['a', 'b', 'c']:
            yield connection

Тоді ви можете або використовувати forоператор, щоб нескінченно повторювати, або ви можете зателефонувати, next()щоб отримати одне наступне значення з ітератора генератора:

connections = circular()
next(connections) # 'a'
next(connections) # 'b'
next(connections) # 'c'
next(connections) # 'a'
next(connections) # 'b'
next(connections) # 'c'
next(connections) # 'a'
#....

Приємно! Звідки це знати, щоб почати спочатку, коли список вичерпано?
user443854

1
@ user443854 while Trueзасоби повторити назавжди
Якоб Кралл

2
@juanchopanza: Так; itertools.cycle- краща відповідь. Це показує, як ви могли написати таку ж функціональність, якщо itertoolsїї немає :)
Jacob Krall

Чи простий генератор також зберігає копію кожного елемента, як itertools.cycleце робить? Або простий генератор буде більш ефективною пам’яттю? За cycleдокументами :Note, this member of the toolkit may require significant auxiliary storage (depending on the length of the iterable).
dthor

2
@dthor цей генератор створює список з трьома елементами і грамотно над ним, потім знищує список і створює новий, на вічність. Ця документація cycleпередбачає, що вхідний ітерабельний перетворюється до того, listяк його генератор запуститься, оскільки iterable"корисний лише за один прохід за набором значень".
Джейкоб Кралл

9

Або ви можете зробити так:

conn = ['a', 'b', 'c', 'd', 'e', 'f']
conn_len = len(conn)
index = 0
while True:
    print(conn[index])
    index = (index + 1) % conn_len

друкує abcdefab c ... назавжди


3

Ви можете виконати це за допомогою append(pop())циклу:

l = ['a','b','c','d']
while 1:
    print l[0]
    l.append(l.pop(0))

або for i in range()цикл:

l = ['a','b','c','d']
ll = len(l)
while 1:
    for i in range(ll):
       print l[i]

або просто:

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

while 1:
    for i in l:
       print i

всі з яких друкуються:

>>>
a
b
c
d
a
b
c
d
...etc.

з трьох я був би схильний до додавання (pop ()) підходу як функції

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

def rotate_servers(servers):
    servers.append(servers.pop(0))
    return servers

while 1:
    servers = rotate_servers(servers)
    print servers[0]

Оновлення цього, тому що це допомогло мені у зовсім іншому випадку використання, коли я просто хочу повторити список декілька разів, кожен раз, коли стартовий елемент просувається на один крок. Моя ситуація використання полягає в перегляді гравців у грі в покер, просуванні дилера шайбою одного гравця вперед на кожен раунд.
Йоган

2

Вам потрібен спеціальний ітератор - я адаптую ітератор з цієї відповіді .

from itertools import cycle

class ConnectionPool():
    def __init__(self, ...):
        # whatever is appropriate here to initilize
        # your data
        self.pool = cycle([blah, blah, etc])
    def __iter__(self):
        return self
    def __next__(self):
        for connection in self.pool:
            if connection.is_available:  # or however you spell it
                return connection

2

Якщо ви хочете провести цикл nразів, ncycles застосуйте рецепт itertools :

from itertools import chain, repeat


def ncycles(iterable, n):
    "Returns the sequence elements n times"
    return chain.from_iterable(repeat(tuple(iterable), n))


list(ncycles(["a", "b", "c"], 3))
# ['a', 'b', 'c', 'a', 'b', 'c', 'a', 'b', 'c']
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.