any way to loop iteration with same item in python? - python

It's a common programming task to loop iteration while not receiving next item. For example:
for sLine in oFile :
if ... some logic ... :
sLine = oFile.next()
... some more logic ...
# at this point i want to continue iteration but without
# getting next item from oFile. How can this be done in python?

I first thought you wanted the continue keyword, but that would of course get you the next line of input.
I think I'm stumped. When looping over the lines of a file, what exactly should happen if you continued the loop without getting a new line?
Do you want to inspect the line again? If so, I suggest adding an inner loop that runs until you're "done" with the input line, which you can then break out of, or use maybe the while-condition and a flag variable to terminate.

Simply create an iterator of your own that lets you push data back on the front of the stream so that you can give the loop a line that you want to see over again:
next_lines = []
def prependIterator(i):
while True:
if next_lines:
yield(next_lines.pop())
else:
yield(i.next())
for sLine in prependIterator(oFile):
if ... some logic ... :
sLine = oFile.next()
... some more logic ...
# put the line back so that it gets read
# again as we head back up to the "for
# statement
next_lines.append(sLine)
If the prepend_list is never touched, then the prependIterator behaves exactly like whatever iterator it is passed: the if statement inside will always get False and it will just yield up everything in the iterator it has been passed. But if items are placed on the prepend_list at any point during the iteration, then those will be yielded first instead before it returns back to reading from the main iterator.

What you need is a simple, deterministic finite state machine. Something like this...
state = 1
for sLine in oFile:
if state == 1:
if ... some logic ... :
state = 2
elif state == 2:
if ... some logic ... :
state = 1

Ugly. Wrap the body in another loop.
for sLine in oFile :
while 1:
if ... some logic ... :
sLine = oFile.next()
... some more logic ...
continue
... some more logic ...
break
A little better:
class pushback_iter(object):
def __init__(self,iterable):
self.iterator = iter(iterable)
self.buffer = []
def next(self):
if self.buffer:
return self.buffer.pop()
else:
return self.iterator.next()
def __iter__(self):
return self
def push(self,item):
self.buffer.append(item)
it_file = pushback_iter(file)
for sLine in it_file:
if ... some logic ... :
sLine = it_file.next()
... some more logic ...
it_file.push(sLine)
continue
... some more logic ...
Unfortunately no simple way of referencing the current iterator.

Instead of looping through the file line by line, you can do a file.readlines() into a list. Then loop through the list (which allows you to look at the next item if you want). Example:
list = oFile.readlines()
for i in range(len(list))
#your stuff
list[i] #current line
list[i-1] #previous line
list[i+1] #next line
you can go to the previous or next line by simply using [i-1] or [i+1]. You just need to make sure that no matter what, i does not go out of range.

You just need to create a variable out of your iterator, and then manipulate that:
% cat /tmp/alt-lines.py
#! /usr/bin/python -tt
import sys
lines = iter(open(sys.argv[1]))
for line in lines:
print line,
lines.next()
% cat /tmp/test
1
2
3
4
% /tmp/alt-lines.py /tmp/test
1
3
...note that in this case we unconditionally do lines.next() so the above fails for files with odd lines, but I assume that isn't going to be the case for you (and adding the error checking is fairly trivial -- just catch and throw away StopIteration on the manual .next()).

You can assign your iterator to an variable then use the .next get te next one.
iter = oFile.xreadlines() # is this the correct iterator you want?
try:
sLine = iter.next()
while True:
if ... some logic ... :
sLine = iter.next()
... some more logic ...
continue
sLine = iter.next()
except StopIterator:
pass

jle's approach should work, though you might as well use enumerate():
for linenr, line in enumerate(oFile):
# your stuff

Related

How to solve StopIteration error in Python?

I have just read a bunch of posts on how to handle the StopIteration error in Python, I had trouble solving my particular example.I just want to print out from 1 to 20 with my code but it prints out error StopIteration. My code is:(I am a completely newbie here so please don't block me.)
def simpleGeneratorFun(n):
while n<20:
yield (n)
n=n+1
# return [1,2,3]
x = simpleGeneratorFun(1)
while x.__next__() <20:
print(x.__next__())
if x.__next__()==10:
break
Any time you use x.__next__() it gets the next yielded number - you do not check every one yielded and 10 is skipped - so it continues to run after 20 and breaks.
Fix:
def simpleGeneratorFun(n):
while n<20:
yield (n)
n=n+1
# return [1,2,3]
x = simpleGeneratorFun(1)
while True:
try:
val = next(x) # x.__next__() is "private", see #Aran-Frey comment
print(val)
if val == 10:
break
except StopIteration as e:
print(e)
break
First, in each loop iteration, you're advancing the iterator 3 times by making 3 separate calls to __next__(), so the if x.__next__()==10 might never be hit since the 10th element might have been consumed earlier. Same with missing your while condition.
Second, there are usually better patterns in python where you don't need to make calls to next directly. For example, if you have finite iterator, use a for loop to automatically break on StopIteration:
x = simpleGeneratorFun(1)
for i in x:
print i

Python 3 Index Error

Consider the following code:
def anadist(string1, string2):
string1_list = []
string2_list = []
for i in range(len(string1)):
string1_list.append(string1[i])
for i in range(len(string2)):
string2_list.append(string2[i])
# Test returns for checking
# return (string1_list,string2_list)
# return len(string1_list)
# return len(string2_list)
for i in range(0,len(string1_list)):
try:
if (string1_list[i]) in string2_list:
com = string1_list.pop(i)
com_index = string2_list.index(com)
string2_list.pop(com_index)
else:
pass
except ValueError:
pass
return string1_list
def main():
str1 = input("Enter string #1 >>> ")
str2 = input("Enter string #2 >>> ")
result = anadist(str1, str2)
print(result)
#Boilerplate Check
if __name__ == "__main__":
main()
Running in Python 3.5.2 raises an IndexError:
Traceback (most recent call last):
File "E:\CSE107L\Practice\anadist.py", line 34, in <module>
main()
File "E:\CSE107L\Practice\anadist.py", line 29, in main
result = anadist(str1, str2)
File "E:\CSE107L\Practice\anadist.py", line 15, in anadist
if (string1_list[i]) in string2_list:
IndexError: list index out of range
And I can't find what is going wrong. I wrote another code similar and that works:
def main():
lst = [1,2,3,4,5]
lst2 = [5,6,7,8,9]
for i in range(len(lst)):
if lst[i] in lst2:
com = lst.pop(i)
lst2_index = lst2.index(com)
lst2.pop(lst2_index)
else:
pass
print(lst)
if __name__ == "__main__":
main()
I feel the error is coming from the way I am forming string1_list. This code is for how many steps it takes to form an anagram of a pair of words.
In some cases, you are shortening string_list1 while you're iterating over it:
if (string1_list[i]) in string2_list:
com = string1_list.pop(i) # string1_list gets shorter here
However, your range doesn't change. It's still going to go from 0 until it counts up to the original length of string1_list (exclusive). This will cause the IndexError any time string1_list.pop(i) is called.
One possible solution would be to use a while loop instead:
i = 0
while i < len(string1_list):
try:
if string1_list[i] in string2_list:
com = string1_list.pop(i)
com_index = string2_list.index(com)
string2_list.pop(com_index)
else:
pass
except ValueError:
pass
i += 1
This will cause the loop termination condition to be checked after each iteration. If you remove some elements from string1_list, it'll still be OK because the loop will terminate before i gets big enough to overrun the bounds of it's container.
Your issue is that you are mutating the string1_list within the for loop by performing string1_list.pop(i). The length of the list is being reduced within the for loop by doing string1_list.pop(i), but you are still iterating over the length of the original list.
Your second lot of code works only because you only pop on the last iteration of the loop.
Your second attempt works simply because a "match" is found only in the last element 5 and as such when you mutate your list lst it is during the last iteration and has no effect.
The problem with your first attempt is that you might remove from the beginning of the list. The counter doesn't get updated and at some point might reach a value that no longer exists in the list (it has been popped). Try running it with no matches in the input and see that it works.
In general, don't mutate your list while iterating through it. Instead of the:
for i in range(len(lst)):
# mutate list
'idiom', you should opt for the:
for i in list(list_object): # or for i in list_object[:]:
which makes a copy first and allows you to mutate the original list_object by keeping mutating and looping separate.

Alternate apporach for using a flag in python

What is the best/alternate pythonic way for the code below,
li=["Header",1,2,3,4,5]
l=0
for i in li:
if l == 0:
.....
l += 1
else:
....
You can replace:
l=0
for i in li:
if l ==0:
frobnosticate(i)
l+= 1
else:
twiddle(i)
With:
#do something with header outside the loop
frobnosticate(li[0])
#loop over everything but the first element
for i in li[1:]:
twiddle(i)
li=["Header",1,2,3,4,5]
do_stuff(li[0]) # first ....
for i in li[1:]:
normal_stuff(i) # second ...
This will deal with header first and then iterate through rest of list.
Accessing such pattern of elements using slicing is good enough. However, if data type in your list is not fixed/known, you may also use dictionary to redirect element to specified function:
def access_string_element(string):
....
def access_integer_element(integer):
....
access_table = dict()
access_table[str] = access_string_element
access_table[int] = access_integer_element
...
Then you could use the dict like this:
for element in li:
access_table[type(element)](element)
This way benefits if you are going to handle list containing data with different data type. It would make your loop looks clearer, and easy to manage.
Have Fun ;)

Process at least one line in Python

I have the following code structure in my Python script:
for fullName in lines[:-1]:
do stuff ...
This processes every line except for the last one. The last line is a company name and all the rest are people's names. This works except in the case where there's no company name. In this case the loop doesn't get executed. There can only be one name if there's no company name. Is there a way to tell Python to execute this loop at least once?
You could do it like this, dynamically setting the end of your range based on the length of lines:
i = len(lines)
i = -1 if i > 1 else i
for fullName in lines[:i]:
dostuff()
How about using Python's conditional:
>>> name=['Roger']
>>> names=['Pete','Bob','Acme Corp']
>>>
>>> tgt=name
>>> for n in tgt if len(tgt)==1 else tgt[:-1]:
... print(n)
...
Roger
>>> tgt=names
>>> for n in tgt if len(tgt)==1 else tgt[:-1]:
... print(n)
...
Pete
Bob
Use an if-statement:
if len(fullName) == 1: # Only one person
dostuff_with_one_person()
else:
for fullName in lines[:-1]:
dostuff()
This is essentially a do-while loop construct in Python.
Essentially, you can have a for loop or while loop with a break condition. For yours, you could do this:
def do_stuff(x):
print(x)
for i,n in enumerate(lst):
do_stuff(n)
if i>=len(lst)-2:
break
Straightforward method. Processing only till the last but one record, if there are more than one records.
for i in xrange(len(lines)):
process lines[i]
if i == len(lines)-2: break
If there is only one element, i will be 0 and that will be processed and i != -1. If There are more than one elements, lets say 2. After processing the first element, i will be 0 and i == (2-2) will be true and breaks out of the loop.

GoTo(basic) program

I'm doing some tutorials online, and I'm stuck with an exercise :Write a function getBASIC() which takes no arguments, and does the following: it should keep reading lines from input using a while loop; when it reaches the end it should return the whole program in the form of a list of strings. Example of list of strings:
5 GOTO 30
10 GOTO 20
20 GOTO 10
30 GOTO 40
40 END
I wrote a program, but it doesn't work, however I will post it too:
def getBASIC():
L=[]
while "END" not in L:
L.append(str(input()))
if str(input()).endswith("END"):
break
return L
Also I notice you that I'm not allowed to use IS or RECURSION .
There are several errors:
you call input() twice without appending it to the list the second time
'END' in L determines whether there is 'END' (whole) line in the list L (there isn't)
Note: input()already returns a str object; you don't need to call str() on its returned value.
To read input until you've got empty line you could:
def getBASIC():
return list(iter(input, ''))
Or to read until END is encountered at the end of line:
def getBASIC():
L = []
while True:
line = input()
L.append(line)
if line.endswith("END"):
break #NOTE: it doesn't break even if `line` is empty
return L
Back when I was learning Pascal, we used a priming read for loops that needed at least one iteration. This still works well in Python (I prefer it to a while True / break loop).
By simply testing the last line in the list (rather than the last line read) we eliminate the need for a variable to store the input and can combine the reading and appending operations.
def getBASIC():
lines = [input("]")] # use Applesoft BASIC prompt :-)
while not lines[-1].upper().rstrip().endswith("END"):
lines.append(input("]"))
return lines
try this one:
def get_basic():
L = []
while True:
line = str( input() )
L.append( line )
if "END" in line:
break
return L
Use raw_input() instead of input(). input() function takes string from standard input and tries to execute it as a python source coode. raw_input() will return a string as expected.
You use input() 2 time inside a loop. That means you await two string to be input inside one cycle iteration. You don't need last condition (if statement) inside your while loop. It'll end up when "END" is encountered in L.
The next code should do the job:
def getBASIC():
L=[]
while True:
inp = raw_input()
L.append(inp)
if inp.endswith('END'):
break
return L
Your code has the following problems.
"while "END" not in L" will not work if your input is "40 END"
In Python 2.7, "input()" is equivalent to "eval(raw_input()))". So, Python is trying to evaluate the "GOTO" statements.
"if str(input()).endswith("END"):" does not append input to L
So, here is an updated version of your function:
def getBASIC():
L = []
while True:
# Grab input
L.append(str(raw_input()))
# Check if last input ends with "END"
if L[-1].endswith("END"):
break
return L

Categories

Resources