1

Learning python, found this code snippet. It reads text from stdin and prints "Done" when it finishes. I've never seen an else for a while loop anywhere else in any other language. The comments on the site where I found this said this was a pythonic way to do similar to a finally but for a while loop.

import sys
while True:
    line = sys.stdin.readline()
    if not line:
        break
else:
    print("Done");

The question is, is this pythonic? The place I work has a pretty strict pep-8 compliance policy, so I need to know if this is okay.

jedwards
  • 29,432
  • 3
  • 65
  • 92
  • 1
    I don't think while-else works that way. See http://stackoverflow.com/questions/3295938/else-clause-on-python-while-statement – Kevin Apr 11 '15 at 01:58
  • Well, using unnecessary parens like `while(True)` isn't Pythonic. If you really want to know about using Pythonic style, you may want to consider running a checker like [`pep8`](https://pypi.python.org/pypi/pep8) to catch all the simple stuff like that, so people can look for the higher-level issues without being distracted. – abarnert Apr 11 '15 at 01:59
  • 1
    Another side note: You almost never need/want to loop over `readline`; just use the file as an iterator. That gives you exactly the behavior you're trying to get with the `while` loop, but without the confusion, and with one line instead of 4-5 lines. – abarnert Apr 11 '15 at 02:16

2 Answers2

6

The basic idea of using while/else is certainly Pythonic—it wouldn't be in the language otherwise.

However, there are two bugs in your code that each mean that your else code never runs. It's probably not Pythonic to include code that never executes and is only there to mislead the reader. :) More importantly, it's probably not what you wanted.


First, there's no way for you to ever exit the loop (except by exception). readline returns an empty string at EOF, so that's probably what you want to check for:

while True:
    line = sys.stdin.readline()
    if line:
        print(line)
    else:
        break
else:
    print("Done")

But next, I'm not sure while/else does what you think it does. I think you're expecting it to run the else however you exit the loop. But as the docs explain:

A break statement executed in the first suite terminates the loop without executing the else clause’s suite.

In other words, the else part only runs if you exit normally, by the condition becoming false. Obviously while True never becomes false.

And that's the whole point: sometimes you need to distinguish between the loop finishing normally vs. exiting with an if something: break; that's when you use else:. If you want to do the same thing either way, just do it:

while True:
    line = sys.stdin.readline()
    if line:
        print(line)
    else:
        break
print("Done")

If you wanted something "similar to a finally" because you may have exceptions, the answer is simple: use a finally:

try:
    while True:
        line = sys.stdin.readline()
        if line:
            print(line)
        else:
            break
finally:
    print("Done")

As a side note, looping over readline with a while and break is almost never necessary; a file-like object like sys.stdin is an iterable of lines, the same lines that readline will return, so you can just do:

for line in sys.stdin:
    print(line)
print("Done")

As a side note, it's generally not considered Pythonic to use unnecessary parentheses and semicolons in your code—especially when it makes your code look misleadingly like C or Java or something. While PEP 8 is only meant as a standard for the Python stdlib, many people use it as a guide for their own Python software, and it's a good idea to follow its recommendations unless you have a good reason not to. You can use tools like pep8 to check whether you're following the guidelines. This only catches the simple stuff, like your while(True): instead of while True and print("Done"); instead of print("Done"), but violating that simple stuff distracts readers from noticing the possible higher-level violations that you're asking about.

abarnert
  • 354,177
  • 51
  • 601
  • 671
0

Indeed, this is a pythonic way to accomplish the goal of your code snippet. It is known as the else-for-last-iteration-of-loop (EFLIOL) idiom.

  • Yep—and the `while` and `for` statements wouldn't _have_ an `else` if it were never Pythonic to use them, would they? :) – abarnert Apr 11 '15 at 01:58
  • Google returns zero results for "else-for-last-iteration-of-loop". Got a citation? (or is this a joke going over my head?) – Kevin Apr 11 '15 at 01:58
  • The problem with this answer, is that the loop in the OP's question is infinite, and contains no `break` statement. So `else` will **never** be reached. Although in general, I agree (for the reasons listed in the link in Kevin's comment). – jedwards Apr 11 '15 at 02:04