28

I'm about 2 weeks deep in my study of Python as an introductory language. I've hit a point in Zed's "Learn Python the Hard Way" where he suggests:

Use a while-loop only to loop forever, and that means probably never. This only applies to Python, other languages are different.

I've googled all over this, referenced everything I can, but I can't find any reason in the world why this would be a convention in Python. What makes it different?

When I gave up programming 10 years ago, I was working in VB and would regularly be told to get rid of my For loops and use While loops instead. I was a hack (as I am today, though I wrote a LOT of code back then), so I just did what I was told without questioning it. Well, now I'm questioning it. Is this a speed issue? Is it just to avoid escape-less infinites?

kratenko
  • 7,354
  • 4
  • 36
  • 61
Random_Person
  • 435
  • 2
  • 7
  • 11
  • 4
    Most of the time, you will be looping to traverse a list, and it is syntactically nicer to use a `for` loop. Also see this: http://stackoverflow.com/questions/920645/when-to-use-while-or-the-for-in-python – wkl Nov 24 '10 at 18:20
  • 3
    If you are having difficulty knowing when to use each type of loop, I would suggest reading up on loop patterns, as I think it will answer your questions. http://www.cs.duke.edu/~ola/patterns/plopd/loops.html – Brandon Nov 24 '10 at 19:14
  • 3
    @Brandon that document is not going to help the questioner. He's asking about how to write idiomatic Python, and that is going to confuse him, since idiomatic Python is often quite different from idiomatic Java or C++. – Paul McMillan Nov 24 '10 at 19:30
  • 1
    @Paul, the document explains that you would use a for loop when the number of data elements is known in advance, and a while loop when the number of elements is unknown. This applies to any imperative language, whether it is Java, C++ or Python. I'm sorry that the document isn't directly written in Python, but the patterns are the same. The fact that Python has a somewhat higher-level for loop doesn't change the pattern. – Brandon Nov 24 '10 at 20:14
  • 8
    @Brandon, It totally changes the pattern. You don't need to know the number of elements in advance to use a `for` loop in python and often you do need to know this when using a `while` loop. – aaronasterling Nov 25 '10 at 01:32
  • 4
    @Brandon: aaronsterling hit it on the head. With python, you can even use `for` loops with generators, where you EXPLICITLY DO NOT KNOW how many elements are being iterated. Where's my -1 wrong moderation for comments when I need it? Python is NOT LIKE Java and C++, which you should know, given your rep score in Python tags. – Paul McMillan Nov 25 '10 at 03:48
  • 1
    @Aaron & Paul: Okay, point taken with generators. When I think of an unknown number of elements in a list, I'm thinking of iterating over a search queue, where I'm adding and removing elements in a while loop until all elements exhausted. I can't think of how you would do that with a for loop, but you guys are clearly on top of all this. – Brandon Dec 07 '10 at 13:45

13 Answers13

27

The advice seems poor to me. When you're iterating over some kind of collection, it is usually better to use one of Python's iteration tools, but that doesn't mean that while is always wrong. There are lots of cases where you're not iterating over any kind of collection.

For example:

def gcd(m, n):
    "Return the greatest common divisor of m and n."
    while n != 0:
        m, n = n, m % n
    return m

You could change this to:

def gcd(m, n):
    "Return the greatest common divisor of m and n."
    while True:
        if n == 0:
            return m
        m, n = n, m % n

but is that really an improvement? I think not.

Gareth Rees
  • 64,967
  • 9
  • 133
  • 163
  • 3
    Zed's advice is still pretty sound. What he's trying to get at is that if you're using a while loop, make SURE it's a case like this, rather than the far more common case where you're iterating over an iterable. We can come up with correct usages for `while` till the cows come home, but the fact is, it's far less common in idiomatic python than `for`. – Paul McMillan Nov 24 '10 at 19:07
  • 22
    But if that was what he wanted to say, then he chose a very misleading way to express it. – Gareth Rees Nov 24 '10 at 19:12
  • 1
    @Gareth Rees: I think it is a good idea to give beginners clear rules. In general, if you teach them right, people will become smart enough to figure out when to break the rules by themselves, but will often be confused by myriads of exceptions whose trade-offs they can't evaluate because they haven't encountered those situations yet. Speaking from personal experience, I wish I had learnt about maps and folds and catamorphisms and sequence comprehensions and generator expressions *first*, before I learnt about loops. Loops really are an anti-pattern. – Jörg W Mittag Nov 24 '10 at 19:25
  • 1
    @Gareth if you've read any of the rest of that, you'll see that this is his approach to the whole text. It's a _very_ introductory text, and he's fond of hyperbole. Most of the advice there is designed to set new programmers on the correct path, under the assumption that they'll figure out when to deviate from it in due course. Whether that's the "right" way to approach an introductory programming text is less clear. – Paul McMillan Nov 24 '10 at 19:25
  • @Jörg I think this is why many good CS programs teach the first class in Scheme or Lisp. – Paul McMillan Nov 24 '10 at 19:27
  • 4
    I haven't read the book, but it looks as if at least one reader failed to appreciate the hyperbole, so maybe it's not such a great pedagogical strategy? – Gareth Rees Nov 24 '10 at 19:31
  • 1
    @Gareth If you've taught any subject at all, you know that a very common teaching pattern is to expound upon the rules of the subject till your student learns them. Then you show them when it's appropriate to break the rules. As instructors, it is counterproductive to get bogged down in the precise application of each technique as it is taught. In Python, a `for` is almost always preferred to a `while`, and I think this is well conveyed. Zed is abrasive, yes, but objecting to an entire didactic technique based on a sample size of one? – Paul McMillan Nov 24 '10 at 19:39
  • 3
    @Paul: If you have ever learned any subject at all, you would know how confusing it is for teachers to present over-simplified rules that have glaring contradictions. It could easily be taught without even showing while loops, or leaving them as a more advanced subject later on rather than introduce contradictory rules. – nate c Nov 24 '10 at 20:35
  • @Gareth The hyperbole was not lost on me, it just inspired a thought pattern that I couldn't escape myself out of, so I asked for assistance--albeit a little earlier in my studies than I am likely inclined to understand. To be fair to Zed, on this same page, after covering a few "rules" he informs the student that these are suggestions and that we will understand as time goes on when these rules may be broken or thrown out altogether. – Random_Person Nov 24 '10 at 20:37
  • 1
    @Paul I have taught undergraduates to program, and I have to say that it never occurred to me that programming had rules of this form, let alone to expound on them! I got my students to write code, and then I would quiz them on it in class: could they explain how their code worked? How long would it take to run? What kind of errors might occur? What alternative data structures and algorithms could they have used? What were the trade-offs for different scenarios? etc. I wanted my students to acquire a repertoire of techniques and analytical skills to choose between them, not follow rules. – Gareth Rees Nov 26 '10 at 01:28
  • @Gareth Agreed. In an interactive setting, that's absolutely the way you want things to work. – Paul McMillan Nov 30 '10 at 21:03
11

It's because in the typical situation where you want to iterate, python can handle it for you. For example:

>>> mylist = [1,2,3,4,5,6,7]
>>> for item in mylist:
...     print item
... 
1
2
3
4
5
6
7

Similar example with a dictionary:

>>> mydict = {1:'a', 2:'b', 3:'c', 4:'d'}
>>> for key in mydict:
...     print "%d->%s" % (key, mydict[key])
... 
1->a
2->b
3->c
4->d

Using a while loop just isn't necessary in a lot of common usage scenarios (and is not "pythonic").


Here's one comparison from a mailing list post that I though was a good illustration, too:

> 2. I know Perl is different, but there's just no equivalent of while
> ($line = <A_FILE>) { } ?

Python's 'for' loop has built-in knowledge about "iterable" objects, and that includes files. Try using:

for line in file:
    ...

which should do the trick.

eldarerathis
  • 35,455
  • 10
  • 90
  • 93
  • I would rarely use a while statement to iterate over a list, in python or any other language. For loop, maybe, but not a while. I don't see how this answer sheds any light on why you would never use a while loop. – Bryan Oakley Nov 24 '10 at 18:24
  • 1
    `for key, value in mydict.iteritems(): print '%d->%s' % (key, value)` – ephemient Nov 24 '10 at 18:27
  • @Bryan: Given that the OP specifically mentioned being told to use while loops everywhere in VB, I was guessing that he probably used one to iterate over an array at some point. – eldarerathis Nov 24 '10 at 18:50
  • 1
    In many languages, `while not EOF` is a pretty common construction, and the pythonic `for line in file:` isn't so natural. – Paul McMillan Nov 24 '10 at 19:15
  • You assumed correctly. I was either iterating over an array, or iterating over a file to build an array. Paul hit it right, I was using `while not EOF` a LOT. Your explanation helped quite a bit in understanding what I used to do with VB5 vs what I can do with Python. – Random_Person Nov 24 '10 at 20:52
9

Python follows the philosophy of

There should be one-- and preferably only one --obvious way to do it.

(see https://www.python.org/dev/peps/pep-0020/ for details).

And in most cases there is a better way to do iterations in Python than using a while loop.

Additionally at least CPython optimizes other kinds of loops and iterations (I consider map and list comprehensions as kinds of iteration) better than while loops.

Example: https://www.python.org/doc/essays/list2str

Use intrinsic operations. An implied loop in map() is faster than an explicit for loop; a while loop with an explicit loop counter is even slower.

I have no explanation why you were told to use while loops instead of for loops in VB - I think it normally leads neither to better readable nor to faster code there.

Nubok
  • 3,502
  • 7
  • 27
  • 47
  • To be honest, the code I was working 10 years ago involved iterating through large text documents and the solution I had hacked together was far more complex than a simple while x != eof : x=readline() (or however that would have been written in basic.) It was a project I worked on for the military and I was the sole developer. It comprised 100% of my "professional" coding experience and unfortunately, my brain never progressed from there with coding as I abandoned software altogether. So in my head I'm stuck with this image of while loops being insanely useful. – Random_Person Nov 24 '10 at 20:49
  • @Nubock - I don't agree that performance is generally a good reason to use map or a list comprehension over a for loop. That said, it may just be that Lisp has fried my brain, but I find list comprehensions more readable. Plus they limit the side-effects your code has. – Jason Baker Nov 25 '10 at 00:44
6

The general issue with while loops is that it is easy to overlook, or somehow skip the statement where the loop variable gets incremented. Even a C for-loop can have this problem - you can write for(int x=0; x<10;), although it is unlikely. But a Python for loop has no such problem, since it takes care of advancing the iterator for you. On the other hand, a while loop forces you to implement a never fail increment.

s = "ABCDEF"
i = 0
while (i < len(s)):
    if s[i] in "AEIOU":
        print s[i], 'is a vowel'
        # oops, this is one level too deep
        i += 1

is an infinite loop as soon as you hit the first non-vowel.

PaulMcG
  • 62,419
  • 16
  • 94
  • 130
4

A while loop is a tool, nothing more. To use it or not use it is a matter of whether it's the right tool for the job, nothing more. You are right to question the advice, it's silly.

That being said, there are constructs in python that obviate the need for while loops some of the time. For example, list comprehensions do things one might ordinarily do with a while or for loop.

Use the right tool for the job, don't try to memorize and stick to a bunch of rules. The goal is to write code that a) works and b) is clear.

Bryan Oakley
  • 370,779
  • 53
  • 539
  • 685
  • 1
    The core of the advice is that `for` loops are far more common in idiomatic python than `while` loops. I don't think that's silly or useless. If the advice makes the asker stop and think "do I REALLY need a `while` loop here, or can I do this more elegantly with a `for`?" then it will have done well. – Paul McMillan Nov 24 '10 at 19:11
  • 2
    @Paul McMillan, In the same vein, if it makes the asker find some way to contort code into a `for` loop when a `while` loop would be far clearer and elegant, than it will have done poorly. There are all sorts of use cases where `while` smokes a `for` loop along almost any useful metric that you care to mention. – aaronasterling Nov 25 '10 at 01:56
4

It is not a speed issue, and it is what it is, a suggestion.

I think the intention is to make the code more explicit and easier to read. If you can write it without a while loop in python, it probably looks cleaner and is easier to understand.

user318904
  • 2,968
  • 4
  • 28
  • 37
3
>>> counter = 0
>>> while counter < 5:
       print(counter)
       counter += 1         
0
1
2
3
4

>>> for x in range(5):
       print x          
0
1
2
3
4

See for yourself, which one do you prefer?

When an easier to understand and easier to read code works, that is the Python-ic way.

Neuron
  • 5,141
  • 5
  • 38
  • 59
user225312
  • 126,773
  • 69
  • 172
  • 181
3

Let me share a secret with you: Nine times out of ten with python, the answer is just "use common sense". It's not a terribly tricky language (aside from some notable gotchas). In this case, I'd consider Zed's rule of thumb superfluous because it's almost always common sense to know what kind of loop to use.

That said, if I run into a situation that requires iteration and I have a choice, I prefer the following (from greatest to least preference):

  1. List/generator/set/dict comprehension
  2. For loop
  3. While loop
  4. Recursion

Comprehensions are simple, but it's surprising how many times you can make a for loop into one if you put thought into it. But generally, I'd say that about 90% of all iteration I'll do is with a list comprehension or a for loop.

Jason Baker
  • 192,085
  • 135
  • 376
  • 510
  • 1
    Yeah... except when your common sense has been perverted by an evil language like VB. Good preference list (though I tend to swap 3 and 4). – Paul McMillan Nov 25 '10 at 03:55
  • I would tend to swap 3 and 4 as well, though naturally in a performance-sensitive area with a reasonable amount of data I'd avoid recursion. – Chris Morgan Nov 25 '10 at 04:33
2

Also there are list comprehensions e.g.:
>>> vec = [2, 4, 6]
>>> [3*x for x in vec]
[6, 12, 18]

basarat
  • 261,912
  • 58
  • 460
  • 511
2

This is an idiom I use frequently:

done = False
while not done:
    done = DoSomeStuffAndReturnTrueIfAllDone()
    # wait a while before polling again for more work
    time.sleep(5)

I could pull this out into a generator, sure, but I don't see that as a win. Are there other pythonic ways to do this?

Edited to clarify that I'm periodically polling for work.

Russell Borogove
  • 18,516
  • 4
  • 43
  • 50
  • 4
    Using `while True` and `if stuff(): break` would avoid the need for the local variable. – Gareth Rees Nov 24 '10 at 18:44
  • 1
    Or more generally, just have your function do stuff and return when it's done. Do the looping inside your dostuff function. – Paul McMillan Nov 24 '10 at 19:10
  • 2
    @Paul, that just moves the loop, it doesn't eliminate it. Also, I frequently want to time.sleep() between iterations, or poll some other subsystems, so there might be a little more to the body of the loop. – Russell Borogove Nov 24 '10 at 19:18
  • 1
    @Russell fair enough. It's hard to make clear points about such abstract snippets. I agree that `while` is more useful when you're doing things that need `sleep()` between them. – Paul McMillan Nov 24 '10 at 19:22
  • 6
    `while not DoSomeStuffAndReturnTrueIfAllDone(): time.sleep(5)` – aaronasterling Nov 25 '10 at 01:59
  • @aaronasterling. Wow. That is totally clean an pythonic and avoids putting the loop into the function leaving open the possibility of polling other sources in the same loop later or in other applications that want to use the function. Much nicer than forcing it into an iterator or moving the loop inside the function. – aaronasterling Nov 25 '10 at 02:01
  • 1
    @Russell If you're building high performance systems, greenlets or stackless python are all the rage for that sort of thing these days. – Paul McMillan Nov 25 '10 at 03:53
2

The for loop is best for looping through a collection. I mostly think of the for loop as a "definite loop." Using a for loop for a collection prevents errors arising from attempting to use indices that are out of bounds or from accidentally using negative indices. Unless you are in a special situation, use a for loop for traversing a collection.

The while loop is most useful for situations where you may have to repeat an action an indefinite number of times. It is the "indefinite loop". The example shown above for calculating a gcd is a prime example of where to use a while loop. You must bear in mind that the predicate in the while must change, or you could get stuck in an infinite loop.

If at all possible, avoid using break to get out of a loop.

ncmathsadist
  • 4,684
  • 3
  • 29
  • 45
1

I think that using infinite loop is conceptually wrong, because an algorithm is, by its definition, composed by a finite numbers of instructions (wikipedia):

In mathematics, computer science, and related subjects, an algorithm [...] is an effective method for solving a problem expressed as a finite sequence of steps.

In my opinion is better to find another solution than the infinite loop whenever possible, and this applies to every programming language past, present and future.

BlackBear
  • 22,411
  • 10
  • 48
  • 86
  • 1
    So, as per your example, if I wanted to grab user input to a keyboard... to make a command line menu... how would I go about this in Python without something like `while chr == '' : if msvcrt.kbhit(); chr = msvcrt.getch()` – Random_Person Dec 01 '10 at 01:31
  • That's ok, I mean, it's wrong doingwhile(true) : ... : if(chr=='') break; – BlackBear Dec 01 '10 at 13:39
0

Why avoid while loops? For training purposes. I quote from the book:

Never be a slave to the rules in real life. For training purposes you need to follow these rules to make your mind strong, but in real life sometimes these rules are just stupid. If you think a rule is stupid, try not using it.

The fact is that some of these rules are stupid all the time, in real life. Avoiding while loops is one. His rule for for loops is that you use them when there is a list (or tuple or generator) of elements to iterate over or a fixed number of iterations. This is correct. When there is not, and you instead have a certain state or condition you want to reach, you use while loops. Only using them when you have an infinite loop (like event handlers) is a stupid rule. But newbies tend to use them too often, maybe that's why he has the rule there.

Lennart Regebro
  • 167,292
  • 41
  • 224
  • 251