Sourabh Saxena
Sourabh Saxena

Reputation: 127

Why while loop is sticking at raw_input? (python)

In the following code i am trying to make a "more" command (unix) using python script by reading the file into a list and printing 10 lines at a time and then asking user do you want to print next 10 lines (Print More..). Problem is that raw_input is asking again and again input if i give 'y' or 'Y' as input and do not continue with the while loop and if i give any other input the while loop brakes. My code may not be best as am learning python.

import sys
import string
lines = open('/Users/abc/testfile.txt').readlines()
chunk = 10
start = 0

while 1:
    block = lines[start:chunk]
    for i in block:
        print i
    if raw_input('Print More..') not in ['y', 'Y']:
        break
    start = start + chunk

Output i am getting for this code is:-

--
10 lines from file

Print More..y
Print More..y
Print More..y
Print More..a

Upvotes: 3

Views: 981

Answers (3)

Ashwini Chaudhary
Ashwini Chaudhary

Reputation: 250881

As @Tim Pietzcker pointed out, there's no need of updating chunk here, just use start+10 instead of chunk.

block = lines[start:start+10]

and update start using start += 10.

Another alternative solution using itertools.islice():

 with open("data1.txt") as f:
    slc=islice(f,5)            #replace 5 by 10 in your case
    for x in slc:
        print x.strip()
    while raw_input("wanna see more : ") in("y","Y"):     
        slc=islice(f,5)        #replace 5 by 10 in your case
        for x in slc:
            print x.strip()

this outputs:

1
2
3
4
5
wanna see more : y
6
7
8
9
10
wanna see more : n

Upvotes: 2

abarnert
abarnert

Reputation: 365627

Instead of explaining why your code doesn't work and how to fix it (because Tim Pietzcker already did an admirable job of that), I'm going to explain how to write code so that issues like this don't come up in the first place.

Trying to write your own explicit loops, checks, and index variables is difficult and error-prone. That's why Python gives you nice tools that almost always make it unnecessary to do so. And that's why you're using Python instead of C.

For example, look at the following version of your program:

count = 10
with open('/Users/abc/testfile.txt', 'r') as testfile:
    for i, line in enumerate(testfile):
        print line
        if (i + 1) % count == 0:
            if raw_input('Print More..') not in ['y', 'Y']:
                break

This is shorter than the original code, and it's also much more efficient (no need to read the whole file in and then build a huge list in advance), but those aren't very good reasons to use it.

One good reason is that it's much more robust. There's very little explicit loop logic here to get wrong. You don't even need to remember how slices work (sure, it's easy to learn that they're [start:stop] rather than [start:length]… but if you program in another language much more frequently than Python, and you're always writing s.sub(start, length), you're going to forget…). It also automatically takes care of ending when you get to the end of the file instead of continuing forever, closing the file for you (even on exceptions, which is painful to get right manually), and other stuff that you haven't written yet.

The other good reason is that it's much easier to read, because, as much as possible, the code tells you what it's doing, rather than the details of how it's doing it.

But it's still not perfect, because there's still one thing you could easily get wrong: that (i + 1) % count == 0 bit. In fact, I got it wrong in my first attempt (I forgot the +1, so it gave me a "More" prompt after lines 0, 10, 20, … instead of 9, 19, 29, …). If you have a grouper function, you can rewrite it even more simply and robustly:

with open('/Users/abc/testfile.txt', 'r') as testfile:
    for group in grouper(testfile, 10):
        for line in group:
            print line
        if raw_input('Print More..') not in ['y', 'Y']:
            break

Or, even better:

with open('/Users/abc/testfile.txt', 'r') as testfile:
    for group in grouper(testfile, 10):
        print '\n'.join(group)
        if raw_input('Print More..') not in ['y', 'Y']:
            break

Unfortunately, there's no such grouper function built into, say, the itertools module, but you can write one very easily:

def grouper(iterator, size):
    return itertools.izip(*[iterator]*size)

(If efficiency matters, search around this site—there are a few questions where people do in-depth comparisons of different ways to achieve the same effect. But usually it doesn't matter. For that matter, if you want to understand why this groups things, search this site, because it's been explained at least twice.)

Upvotes: 3

Tim Pietzcker
Tim Pietzcker

Reputation: 336108

You're constructing your slices wrong: The second parameter in a slice gives the stop position, not the chunk size:

chunk = 10
start = 0
stop = chunk
end = len(lines)
while True:
    block = lines[start:stop]     # use stop, not chunk!
    for i in block:
        print i
    if raw_input('Print More..') not in ['y', 'Y'] or stop >= end:
        break
    start += chunk
    stop += chunk

Upvotes: 5

Related Questions