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.
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'
#....
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'
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
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
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]
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']
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
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))
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))
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.
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'
#....
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'
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
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
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]
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']
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
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))
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))