Devpuff
Pricing
Log InStart Free
  1. Home
  2. Blog
  3. Python while loops (and how not to write infinite ones)
Learn

Python while loops (and how not to write infinite ones)

A Python while loop repeats while a condition is true. Counters, while True with break, the else clause, and why a 0.1 step can make a loop run forever.

By Max Arthur
Co-Founder & Content Marketer·October 22, 2026·9 Min read
White text reading 'While loops in Python' on a dark purple dotted background

A Python while loop repeats a block of code for as long as a condition is true. Python checks the condition before every pass; the moment it is false, the loop ends and the program carries on with the line after it:

n = 0
while n < 3:
    print(n)
    n += 1
print("done")
0
1
2
done

Every while loop has three parts: a condition, a body, and something inside the body that eventually makes the condition false. Here that third part is n += 1. Forget it and the loop never ends.

Most of this post is about that third part, because it is where while loops go wrong. Every output below was measured in Pyodide running Python 3.13.2, the same runtime Devpuff's Python lessons use.

while vs for: which loop to use

Python has two loops, and they answer different questions. A for loop goes through something you already have. A while loop keeps going until something changes.

Situation Use
Every item in a list, string or dictionary for
A fixed number of times for with range()
Until a condition changes (a balance doubles, a queue empties) while
Ask again until the answer is valid while True with break

If you can say how many times the loop will run before it starts, reach for a for loop. Counting with while works, but it is slower and easier to get wrong. Summing the numbers 0 to 99,999 ten times took 103.9ms with a while loop and a counter, 81.6ms with for over range(), and 56.2ms with sum(range(...)). The for version is faster because range does the counting in C, while the while version runs i < 100000 and i += 1 as Python code on every pass.

The counter pattern, and what the variable holds afterwards

n = 0
while n < 3:
    n += 1
print(n)   # 3

The body ran three times, when n was 0, 1 and 2. After the loop, n holds the first value that failed the test, which is 3. Changing < to <= runs the body a fourth time, and that one-character difference is the source of most off-by-one bugs in while loops.

The pattern earns its place when you cannot know the count in advance. How many years does it take money to double at 7% a year?

balance = 1000
years = 0
while balance < 2000:
    balance *= 1.07
    years += 1
print(years)               # 11
print(round(balance, 2))   # 2104.85

You could not write that as a for loop without already knowing the answer.

Another example is counting the digits in a number by chopping one off each pass:

n = 12345
digits = 0
while n > 0:
    n //= 10
    digits += 1
print(digits)   # 5

Try it with n = 0 and the answer is 0, because the condition is false before the first pass and the body never runs. A while loop can run zero times, and that is often the edge case that breaks it. Here the fix is to treat zero separately.

Infinite loops: the three ways to write one by accident

1. Forgetting to update the variable.

n = 0
while n < 3:
    print(n)
    # forgot n += 1, so this prints 0 forever

In a terminal, press Ctrl+C to stop it, which raises KeyboardInterrupt. In a browser-based Python runtime like the one in Devpuff's lessons there is often no stop button that can reach a running loop, and the tab freezes. Read your loop for the update line before you run it.

2. Updating in the wrong direction. Writing n -= 1 in a loop that waits for n to reach 3 moves it further away on every pass.

3. Comparing floats for exact equality. This one looks correct and is not:

x = 0.0
steps = 0
while x != 1.0 and steps < 20:   # the cap is only here so the example ends
    x += 0.1
    steps += 1
print(steps)   # 20
print(x)       # 2.0000000000000004

Adding 0.1 ten times does not give exactly 1.0. It gives 0.9999999999999999, because 0.1 has no exact binary representation. So x steps straight past 1.0, the condition x != 1.0 stays true for ever, and only the safety cap stopped it. Without the cap, this loop never ends.

Switching to < stops the loop, but look at the count:

x = 0.0
steps = 0
while x < 1.0:
    x += 0.1
    steps += 1
print(steps)   # 11

Eleven passes, not ten, because after ten additions x is still a hair under 1.0. The reliable fix is to count with integers and work out the float from the count, for example x = i / 10.

A safety cap for loops that depend on the outside world

When a loop waits on something you do not control, such as a network call, a retry or a value from a file, give it a maximum. The else clause, covered properly below, reports when the cap was hit:

def connect(attempt):
    return attempt == 3   # pretend the third try succeeds

attempts = 0
while attempts < 5:
    attempts += 1
    if connect(attempts):
        print("connected on attempt", attempts)
        break
else:
    print("gave up after", attempts, "attempts")
connected on attempt 3

Change connect to always return False and the output becomes gave up after 5 attempts. Either way, the loop is guaranteed to end.

while True and break

Sometimes the natural place to decide whether to stop is in the middle of the body, not at the top. The idiom is an infinite loop with a break where the decision belongs:

while True:
    reply = input("Number or quit: ")
    if reply == "quit":
        break
    print("Double:", int(reply) * 2)

Typing 7 and then quit prints Double: 14 and ends the loop. (input() needs a terminal to type into. Devpuff's browser lessons feed answers in for you, which is how this example was run.)

Python has no do-while loop. do: is a SyntaxError: invalid syntax. while True with a break is the equivalent, and it guarantees the body runs at least once.

A while True loop is only safe when every path through the body either reaches a break or makes progress towards one. Check that before you run it.

continue, and the infinite loop it can cause

continue skips the rest of the body and jumps back to the condition:

i = 0
evens = []
while i < 10:
    i += 1
    if i % 2:
        continue
    evens.append(i)
print(evens)   # [2, 4, 6, 8, 10]

The order of the first two lines inside the loop matters. Here i += 1 comes before the continue, so every pass makes progress. Move the increment to the bottom of the body and the first odd number hits continue, skips the increment, and loops on i = 1 for ever. In a for loop that cannot happen, because the loop itself fetches the next value. In a while loop, you are responsible for it.

The else clause

A while loop can have an else block. It runs when the condition becomes false, and it is skipped when the loop ends with break:

nums = [3, 7, 9]
target = 12
i = 0
while i < len(nums):
    if nums[i] == target:
        print("found", target)
        break
    i += 1
else:
    print("not found", target)
not found 12

With target = 7 the loop breaks and prints found 7, and the else is skipped. Read else on a loop as "if no break happened". The for loop has the same clause with the same rule.

Loop while a collection has items

An empty list is falsy, so while some_list: means "while there is anything left":

queue = [1, 2, 3]
processed = []
while queue:
    processed.append(queue.pop())
print(processed)   # [3, 2, 1]
print(queue)       # []

Each pass removes one item, so the loop is guaranteed to end. list.pop() takes from the end, which is fast. Taking from the front with pop(0) shifts every remaining item along, so for a real queue use collections.deque and popleft().

The same idea removes every copy of a value:

nums = [1, 2, 2, 3, 2]
while 2 in nums:
    nums.remove(2)
print(nums)   # [1, 3]

It works, but each pass searches the list from the start, so it slows down badly on long lists. A comprehension, [n for n in nums if n != 2], does the same job in one pass.

A worked example: guess the number by halving

A while loop shines when each pass narrows down the problem and you stop the moment you are done. Here is the classic number-guessing game, played by a program that always guesses the middle of the range that is still possible:

secret = 73
low, high = 1, 100
guesses = 0
while True:
    guess = (low + high) // 2
    guesses += 1
    if guess == secret:
        break
    elif guess < secret:
        low = guess + 1
    else:
        high = guess - 1
print(guesses, guess)   # 6 73

Each wrong guess cuts the remaining range in half, so the loop is guaranteed to end: the range shrinks on every pass until it holds one number. Running it for every secret from 1 to 100, the worst case took 7 guesses. That is binary search, the same idea behind fast lookups in sorted data, and the Big O notation post explains why halving makes it so quick.

The walrus operator in a while condition

The := operator assigns and tests in one step, which suits loops that fetch a value and stop when there are none left:

data = iter(["a", "b", "", "c"])
got = []
while (item := next(data)):
    got.append(item)
print(got)   # ['a', 'b']

The loop stops at the first falsy value, the empty string, so "c" is never seen. That is exactly right for reading a file in chunks, as in while chunk := f.read(1024):, because an empty chunk means the end of the file. It is a bug when a legitimate value can be falsy, such as 0 in a list of numbers.

Quick reference

while n < 3:            # repeat while true; checked before every pass
    n += 1              # make progress, or it never ends

while True:             # decide in the middle
    if done():
        break

while queue:            # until a list is empty
    queue.pop()

while attempts < 5:     # cap loops that wait on the outside world
    ...
else:                   # runs only if no break happened
    ...

while x < 1.0:          # never use != with floats

Want to practise loops with code that runs in your browser? Start the Python track.

Keep reading

More from the blog

White text reading 'What if name equals main does in Python' on a dark purple dotted background
October 27, 2026·8 min readLearn

What if __name__ == '__main__' actually does in Python

In Python, if __name__ == '__main__' runs code only when you run the file directly, not when it is imported. What __name__ holds and when you need it.

Read more
White text reading 'How to reverse a list in Python' on a dark purple dotted background
October 26, 2026·8 min readLearn

How to reverse a list in Python (and a string)

To reverse a list in Python, use reverse() to change it in place, [::-1] for a reversed copy, or reversed() to loop backwards. Which to use and why.

Read more
{ }
✦

Ready to write some code?

Put this into practice - start your first free lesson. No setup, no credit card.

Start learning free
Devpuff

Learn to code by doing. One tiny, playful lesson at a time.

Learn
ProgramsCoursesPricing
Company
AboutBlogResourcesAffiliates
Support
Help CenterContactStatus
Programs
Frontend DevelopmentFull-Stack DevelopmentReact DeveloperPython Developer
Courses
Advanced ReactAsync JavaScriptAsync PythonCSS BasicsCSS LayoutDSA Basics
Learn to Code
Learn JavaScriptLearn PythonLearn SQLLearn HTMLLearn CSSLearn ReactBrowse All Topics
Platform Comparisons
Devpuff vs CodecademyDevpuff vs Mimo
© 2026 Devpuff. All rights reserved.Privacy PolicyTerms and ConditionsCookies PolicyRefund Policy
Devpuff vs Sololearn
Devpuff vs freeCodeCamp
Read All Comparisons