Restart an iteration of a for loop in python - python

I am am looking to restart an iteration if i receive an exception. (... it is reading data from a server, and occasionally gets intermittent error codes, that do not repeat on a retrying).
with open(input, 'rb') as f:
r = unicodecsv.reader(f)
for row in r:
code to request some data from server
if response_code == 200:
code to process response
else:
want to restart the iteration for the current row
If i was using a while loop, this sort of thing would obviously be obvious (e.g. don't increment the number), but given I am iterating over row in a for loop, can't think of a way of forcing a re-do of the current iteration.
Although there are lots of similar sounding titled posts (e.g. how to restart "for" loop in python ? , Python - Way to restart a for loop, similar to "continue" for while loops? , python: restarting a loop ) each of the ones I have found/read seems to get at something different (e.g. just how to restart when get to the end, rather than restarting an iteration should a certain condition occur).
[Python 2.7]

You can add a second loop like so:
for row in r:
while True:
do stuff
if error:
continue
else:
break

This will restart the for-loop at 4 when the iteration reaches 5 and continue with seemingly no break.
def testloop(start, fault_data, maxrange=10, reset_fault_data=0):
for x in range(start, maxrange):
if x == fault_data:
x -= 1
testloop(x+1, reset_fault_data, maxrange)
break
print(x, end=", " if not x == maxrange-1 else " ")
testloop(1, 5, 21)
output:
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20
without this:
testloop(x+1, reset_fault_data, maxrange)
break
output:
1, 2, 3, 4, 4, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20

Related

Python: How to limit print output to increments with user input control

Let's say that we have a long list of items and we would only like to print 10 at a time before asking the user whether to display more items. What would be the most efficient way to iterate through the list and print 10 items at a time? Would slicing be the answer here?
Used a list of integers as example. Typing 'yes' continues to print, anything else, ends the cycle.
list_name = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30]
for index, element in enumerate(list_name):
if(index % 10 == 0) and index is not 0:
print("Keep Printing Elements?")
answer = input()
if(answer != "yes"):
break
print(element)
def printNextTenItems(list, times):
print(list[10*times: 10*times + 10])
times = 0
keepGoing = True
while (keepGoing):
printNextTenItems(list, times)
times += 1
keepGoing = input("Wanna see more?")
we also can make a generator and just call next when need another portion of data:
def get_next_n(lst,n):
i,l = 0,len(lst)
while i<l:
yield lst[i:i+n]
i+=n
next_10 = get_next_n(my_items_list, 10)
while input()=='Y':
print(*next(next_10,['empty'])) # prints "empty" if all items were displayed

Order of execution in nested loops in python [duplicate]

This question already has answers here:
Why does python use 'else' after for and while loops?
(24 answers)
Why does this `else` block work yet it is not on the same level as the `if` case? [duplicate]
(3 answers)
Closed 3 years ago.
I cant understand the difference between those two code blocks, first the correct one is:
number_list=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]
prime_list=[]
for i in number_list:
for j in range(2,i):
if i%j==0:
break
else: prime_list.append(i)
print('Primes are:',prime_list)
Output is:
Primes are: [1, 2, 3, 5, 7, 11, 13]
but moving the else statement forward inside the block below the if statement (which i thought was the right thing to do) results in a different and wrong output:
number_list=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]
prime_list=[]
for i in number_list:
for j in range(2,i):
if i%j==0:
break
else: prime_list.append(i)
print('Primes are:',prime_list)
Output is:
Primes are: [3, 5, 5, 5, 7, 7, 7, 7, 7, 9, 11, 11, 11, 11, 11, 11, 11, 11, 11, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13]
Why is this so? how does the code behave with the else statement indented?
In the first example else is used for: for loop. It means that the code inside else statement is executed only if for loop ends without break.
So in your case only of any number from the range (2, i) is not a divider of i.
In the second case else is used for: if statement. It means that: if if is not true, then execute else statement. In this scenario let's assume i=5. For:
j = 2 => i%j = 1 => else statement is executed: prime_list.append(5)
j = 3 => i%j = 2 => else statement is executed: prime_list.append(5)
j = 4 => i%j = 1 => else statement is executed: prime_list.append(5)
And thats why you have 3 times 5 in your list.
The else in different scope has difference means:
else with the same scope of your for-loop is the else condition. It will be execute when not-break in your for loop.
else in the for-loop is used to exit the for loop in your second case. The else is related on your if statement
Because when moving in, it will loop and most of the case exit the first if statement, then since the else says to add the value, it would keep adding to it.
There is other ways to do this and more efficiently, like below:
prime_list=[i for i in number_list if all(i % x for x in range(2, i))][1:]
print('Primes are:',prime_list)

Working around evaluation time discrepancy in generators

I found myself running into the gotcha under 'evaluation time discrepancy' from this list today, and am having a hard time working around it.
As a short demonstration of my problem, I make infinite generators that skip every nth number, with n going from [2..5]:
from itertools import count
skip_lists = []
for idx in range(2, 5):
# skip every 2nd, 3rd, 4th.. number
skip_lists.append(x for x in count() if (x % idx) != 0)
# print first 10 numbers of every skip_list
for skip_list in skip_lists:
for _, num in zip(range(10), skip_list):
print("{}, ".format(num), end="")
print()
Expected output:
1, 3, 5, 7, 9, 11, 13, 15, 17, 19,
1, 2, 4, 5, 7, 8, 10, 11, 13, 14,
1, 2, 3, 5, 6, 7, 9, 10, 11, 13,
Actual output:
1, 2, 3, 5, 6, 7, 9, 10, 11, 13,
1, 2, 3, 5, 6, 7, 9, 10, 11, 13,
1, 2, 3, 5, 6, 7, 9, 10, 11, 13,
Once I remembered that great feature, I tried to "solve" it by binding the if clause variable to a constant that would be part of the skip_list:
from itertools import count
skip_lists = []
for idx in range(2, 5):
# bind the skip distance
skip_lists.append([idx])
# same as in the first try, but use bound value instead of 'idx'
skip_lists[-1].append(x for x in count() if (x % skip_lists[-1][0]) != 0)
# print first 10 numbers of every skip_list
for skip_list in (entry[1] for entry in skip_lists):
for _, num in zip(range(10), skip_list):
print("{}, ".format(num), end="")
print()
But again:
1, 2, 3, 5, 6, 7, 9, 10, 11, 13,
1, 2, 3, 5, 6, 7, 9, 10, 11, 13,
1, 2, 3, 5, 6, 7, 9, 10, 11, 13,
Apart from an actual solution, I would also love to learn why my hack didn't work.
The value of idx is never looked up until you start iterating on the generators (generators are evaluated lazily), at which point idx = 4 the latest iteratee value, is what is present in the module scope.
You can make each appended generator stateful in idx by passing idx to a function and reading the value from the function scope at each generator's evaluation time. This exploits the fact that the iterable source of a generator expression is evaluated at the gen. exp's creation time, so the function is called at each iteration of the loop, and idx is safely stored away in the function scope:
from itertools import count
skip_lists = []
def skip_count(skip):
return (x for x in count() if (x % skip) != 0)
for idx in range(2, 5):
# skip every 2nd, 3rd, 4th.. number
skip_lists.append(skip_count(idx))
Illustration of generator expression's iterable source evaluation at gen. exp's creation:
>>> (i for i in 5)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'int' object is not iterable
Your case is a bit trickier since the exclusions are actually done in a filter which is not evaluated at the gen exp's creation time:
>>> (i for i in range(2) if i in 5)
<generator object <genexpr> at 0x109a0da50>
The more reason why the for loop and filter all need to be moved into a scope that stores idx; not just the filter.
On a different note, you can use itertools.islice instead of the inefficient logic you're using to print a slice of the generator expressions:
from itertools import islice
for skip_list in skip_lists:
for num in islice(skip_list, 10):
print("{}, ".format(num), end="")
print()

Find lists which together contain all values from 0-23 in list of lists python

I have a list of lists. The lists within these list look like the following:
[0,2,5,8,7,12,16,18], [0,9,18,23,5,8,15,16], [1,3,4,17,19,6,13,23],
[9,22,21,10,11,20,14,15], [2,8,23,0,7,16,9,15], [0,5,8,7,9,11,20,16]
Every small list has 8 values from 0-23 and there are no value repeats within a small list.
What I need now are the three lists which have the values 0-23 stored. It is possible that there are a couple of combinations to accomplish it but I do only need one.
In this particular case the output would be:
[0,2,5,8,7,12,16,18], [1,3,4,17,19,6,13,23], [9,22,21,10,11,20,14,15]
I thought to do something with the order but I'm not a python pro so it is hard for me to handle all the lists within the list (to compare all).
Thanks for your help.
The following appears to work:
from itertools import combinations, chain
lol = [[0,2,5,8,7,12,16,18], [0,9,18,23,5,8,15,16], [1,3,4,17,19,6,13,23], [9,22,21,10,11,20,14,15], [2,8,23,0,7,16,9,15], [0,5,8,7,9,11,20,16]]
for p in combinations(lol, 3):
if len(set((list(chain.from_iterable(p))))) == 24:
print(p)
break # if only one is required
This displays the following:
([0, 2, 5, 8, 7, 12, 16, 18], [1, 3, 4, 17, 19, 6, 13, 23], [9, 22, 21, 10, 11, 20, 14, 15])
If it will always happen that 3 list will form numbers from 0-23, and you only want first list, then this can be done by creating combinations of length 3, and then set intersection:
>>> li = [[0,2,5,8,7,12,16,18], [0,9,18,23,5,8,15,16], [1,3,4,17,19,6,13,23], [9,22,21,10,11,20,14,15], [2,8,23,0,7,16,9,15], [0,5,8,7,9,11,20,16]]
>>> import itertools
>>> for t in itertools.combinations(li, 3):
... if not set(t[0]) & set(t[1]) and not set(t[0]) & set(t[2]) and not set(t[1]) & set(t[2]):
... print t
... break
([0, 2, 5, 8, 7, 12, 16, 18], [1, 3, 4, 17, 19, 6, 13, 23], [9, 22, 21, 10, 11, 20, 14, 15])
Let's do a recursive solution.
We need a list of lists that contain these values:
target_set = set(range(24))
This is a function that recursively tries to find a list of lists that match exactly that set:
def find_covering_lists(target_set, list_of_lists):
if not target_set:
# Done
return []
if not list_of_lists:
# Failed
raise ValueError()
# Two cases -- either the first element works, or it doesn't
try:
first_as_set = set(list_of_lists[0])
if first_as_set <= target_set:
# If it's a subset, call this recursively for the rest
return [list_of_lists[0]] + find_covering_lists(
target_set - first_as_set, list_of_lists[1:])
except ValueError:
pass # The recursive call failed to find a solution
# If we get here, the first element failed.
return find_covering_lists(target_set, list_of_lists[1:])

Python: Create a range of ordered numbers skipping the inverse of every Nth through Nth+D number

Greetings stackoverflow friends. I've decided to get a little wild this evening and party with for loops to iterate through a list I have created.
It appears the party has been pooped on, though, as the manner through which I would like to create a range is not readily apparent, neither through research nor playing around, and proving bothersome
The Desire: I would like to create a range of numbers much in a similar way that a range is usually created... by specifying range(start, stop, step) but with the minor alteration that I may additionally specify a step 'sweep' value such that range performed more like range(start, stop, step:sweep)
That is to say, if the glorious function above existed it could be used as following;
range(0,16,3:5)
# [0,3,4,5,8,9,10,13,14,15]
Another example!
range(0,24,2:9)
# [0,2,3,4,5,6,7,8,9,11,12,13,14,15,16,17,18,20,21,22,23]
Yet another!
range(0,24,3:9)
# [0,3,4,5,6,7,8,9,12,13,14,15,16,17,18,21,22,23]
Last one.
swept_range(5,20,3,4)
# [7, 8, 11, 12, 15, 16, 19]
In English, I desire a simple way to create a range of ordered numbers holding on to every Nth through Nth + D number group where D is some positive number.
I've looked at slices to no avail.
I know MATLAB can succinctly do this but wasn't sure this exists in Python - does anyone?
How about this generator, using modular arithmetic:
def swept_range(start, stop, step=1, sweep=1):
for i in range(start, stop):
if not 0 < i % sweep < step:
yield i
You could also use a list comprehension, if you need a sequence, rather than an iterator:
def swept_range(start, stop, step=1, sweep=1):
return [i for i in range(start, stop) if not 0 < i % sweep < step]
def yrange(st, sp, N, D):
return [st] + [j for i in range(st,sp,D) for j in range(i+N,i+D+1) if j < sp]
print yrange(0, 16, 3, 5)
# [0, 3, 4, 5, 8, 9, 10, 13, 14, 15]
print yrange(0, 24, 2, 9)
# [0, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 13, 14, 15, 16, 17, 18, 20, 21, 22, 23]
print yrange(0, 24, 3, 9)
# [0, 3, 4, 5, 6, 7, 8, 9, 12, 13, 14, 15, 16, 17, 18, 21, 22, 23]
def srange(start, stop, step=1, sweep=0):
if sweep < 0 :
raise Exception("sweep needs to be positive.")
STEPPING = 0
SWEEPING = 1
state = STEPPING
next = start
res = []
while next < stop:
res.append(next)
#ignores state if sweep is 0
if state == STEPPING or sweep == 0 :
state = SWEEPING
next = next + step
elif state == SWEEPING :
next = next + 1
if next % sweep == 0:
state = STEPPING
return res

Categories