Circular list iterator in Python

Question:

I need to iterate over a circular list, possibly many times, each time starting with the last visited item.

The use case is a connection pool. A client asks for connection, an iterator checks if pointed-to connection is available and returns it, otherwise loops until it finds one that is available.

How can I do this neatly in Python?


If you instead need an immediately created list of the results up to a certain length, rather than iterating on demand: see Repeat list to max number of elements for general techniques, and How to replicate array to specific length array for Numpy-specific techniques.

Asked By: user443854

||

Answers:

The correct answer is to use itertools.cycle. But, let’s assume that library function doesn’t exist. How would you implement it?

Use a generator:

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

Then, you can either use a for statement to iterate infinitely, or you can call next() to get the single next value from the generator iterator:

connections = circular()
next(connections) # 'a'
next(connections) # 'b'
next(connections) # 'c'
next(connections) # 'a'
next(connections) # 'b'
next(connections) # 'c'
next(connections) # 'a'
#....
Answered By: Jacob Krall

Use itertools.cycle, that’s its exact purpose:

from itertools import cycle

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

pool = cycle(lst)

for item in pool:
    print item,

Output:

a b c a b c ...

(Loops forever, obviously)


In order to manually advance the iterator and pull values from it one by one, simply call next(pool):

>>> next(pool)
'a'
>>> next(pool)
'b'
Answered By: Lukas Graf

You need a custom iterator — I’ll adapt the iterator from this answer.

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
Answered By: Ethan Furman

Or you can do like this:

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

prints a b c d e f a b c… forever

Answered By: viky.pat

you can accomplish this with append(pop()) loop:

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

or for i in range() loop:

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

or simply:

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

while True:
    for i in l:
       print i

all of which print:

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

of the three I’d be prone to the append(pop()) approach as a function

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

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

while True:
    servers = rotate_servers(servers)
    print servers[0]
Answered By: litepresence

If you wish to cycle n times, implement the ncycles itertools recipe:

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']
Answered By: pylang

In order to avoid infinite loop, I have used length of array to iterate only until size of list is double.You can implement your own pre condition .Idea is to avoid infinite loop.

#Implement Circular Linked List
from itertools import cycle
list=[1,2,3,4,5]
lstlength=len(list)*2
print(lstlength)
pool=cycle(list)
i=0
#To avoid infinite loop break when you have iterated twice size of the list
for items in pool:
    print(items)
    if i >lstlength:
        break
    i += 1
Answered By: user7258708
class A(object):
    def __init__(self, l):
        self.strt = 0
        self.end = len(l)
        self.d = l

    def __iter__(self):
        return self

    def __next__(self):
        val = None
        if self.strt>=self.end:
            self.strt=0
        val = self.d[self.strt]
        self.strt += 1
        return val

a= A([8,9,7,66])
print(next(a))
print(next(a))
print(next(a))
print(next(a))
print(next(a))
print(next(a))
print(next(a))
print(next(a))
print(next(a))
print(next(a))
Answered By: Argus Malware

For those, who may be interested.
To loop forward or backward starting from given index:

def loop_fwd(arr, index):
  while True:
    arr_index = index % len(arr)
    yield arr_index, arr[arr_index]
    index += 1


def loop_bcw(arr, index):
  while True:
    arr_index = index % len(arr)
    yield arr_index, arr[arr_index]
    index -= 1


forward_it = loop_fwd([1,2,3,4,5], 3)
backward_it = loop_bcw([1,2,3,4,5], 3)

print('forward:')
for i in range(10):
  print(next(forward_it))


print('backward:')
for i in range(10):
  print(next(backward_it))
Answered By: Dmitriy
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.