2

Im trying to get the following output from a while loop to show the progress.

  - ########

Below is the code,

print '  - #',
x = 1
while x < 15:
    print '#',
    time.sleep(0.5)
    x += 1

But Im just getting all of the hashs printed once the while loop is complete (?)

Any ideas

1

5 Answers 5

5
import sys
import time
while True:
    sys.stdout.write('#')
    sys.stdout.flush()
    time.sleep(0.5)
Sign up to request clarification or add additional context in comments.

Comments

4

There are plenty of methods to this.

  1. If you are working with Python 2.X, you need to call sys.stdout.flush() after every print.

  2. If you are using python 3 then you can get it done by doing

    print(#,sep = ' ', flush=True)

  3. You can disable the buffering completely if you run python with -u option: python -u script.py

    If you run your script directly i.e. ./script.py then specify it in the shebang line: #!/usr/bin/env python -u.

  4. The other way to do it is to set env variable PYTHONUNBUFFERED from the bash shell: export PYTHONUNBUFFERED="aa"

Adding more details to cause of the issue:

Stdout in Python is line buffered. Meaning if you had skipped the "," in your print your output will be printed as expected. But when the output is printed in the same line then it will be flushed only if there is a new line or the buffer is full. In case you redirect your output to a file instead of terminal (python script.py >output), output will be flushed only when the buffer is full. This can be verified if you pipe the output to cat: python script.py | cat

In all the methods mentioned in this answer and other answers we are explicitly telling Python to not buffer but flush the output as soon as it gets it.

More research on the topic:

This behavior is not Python Specific. This is the case with printf in C as well. It is due to glibc implementation of linux. The reasoning behind this behavior is efficiency. Because the output is buffered number of read and write operations are minimized. I found this article which gives a brief description of the same: Unix Buffering Delays

Comments

2

I think you already got your answer but in case you later want to overwrite the same line, add a carriage return \r before the characters you're printing (to return your typewriter's carriage to the start of the current line, of course ;) ). I use this to have my file processing scripts write to the same line - each new line overwriting the last. Some fiddling with additonal white space is necessary if the new line is shorter than the previous.

Eg. to make a rotating loader thingy that's eventually replaced by a growing line of #s:

import sys
import time

# rotating loader "animation"
# Ubuntu or the aptitude package manager uses something similar
x = 1
symbols = ["|", "/", "-", "\\"]
while x < 20:
    x+=1
    sys.stdout.write("\r" + symbols[x%len(symbols)])
    sys.stdout.flush()
    time.sleep(0.2)


# progress bar "animation" using hashtags 
x = 1
sys.stdout.write("\r")
while x < 10:
    x+=1
    sys.stdout.write('#')
    sys.stdout.flush()
    time.sleep(0.2)

print("")

3 Comments

Why don't you use for _ in range(20): and for _ in range(10): instead of using while loops?
@zondo Just because I copied it straight from an answer to the question and didn't think too much about it. Besides, I needed the iterator in this case (although there are many other ways of doing it that don't involve a lookup in symbols). But why don't you?
Yeah, I just realized that ;) I edited my answer just a minute ago.
2

print by default writes to stdout which is stored in sys.stdout. Stdout is flushed when a new line is printed. Since you aren't printing the new line, the text isn't displayed until you do. You can, however, flush stdout yourself:

import sys
import time
print '  - #',
sys.stdout.flush()
x = 1
while x < 15:
    print '#',
    sys.stdout.flush()
    time.sleep(0.5)
    x += 1

You really don't need a while loop. A for loop would be shorter:

import sys
import time
print '  - #',
sys.stdout.flush()
x = 1
for _ in range(15):
    print '#',
    sys.stdout.flush()
    time.sleep(0.5)

3 Comments

You seemed interested in iterating without returning an iterator. Instead of using _ as a variable name, you could use for _ in itertools.repeat(None, 15): which actually returns None. It might not matter here at all but it's faster and not simply an indication to the reader that the variable isn't being used.
@jDo: That's true; I hadn't thought of that. In something this small, though, I don't think the saved time is enough to warrant the extra import. I'd like to keep it simple for the OP.
yeah, just a side note. In this case it's not that important unless OP is planning on using it for writing thousands of lines and not telling us (I don't think so)
0

Have a look at tqdm. It displays for you a good looking progress bar very easily.

from tqdm import tqdm
for i in tqdm(range(9)):
    ...

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.