Python tutorials  /  Python Loops (for and while)
Chapter 6 · Python

Python Loops (for and while)

A loop in Python is a control flow structure that repeatedly executes a block of code either a specific number of times or as long as a condition remains true. Python provides two main looping constructs: the 'for' loop, primarily designed to iterate directly over the elements of a sequence (like a list, string, or range), and the 'while' loop, which repeats based on a condition being true, checked before each iteration.

Think of a Python 'for' loop like going through a stack of papers one by one, from the first to the last, doing something with each individual paper as you go — you're not counting up numbers yourself, you're directly working through each item in a collection. A 'while' loop, on the other hand, is more like continuing to refill your water bottle 'while it's not yet full', checking the condition before each attempt, without knowing in advance exactly how many refills it will take.

Consider a Python script that processes a folder of customer invoice files. A 'for' loop would iterate directly over the list of filenames returned by the operating system (e.g., 'for filename in invoice_files:'), processing each invoice one by one without needing a separate counter variable. Meanwhile, a 'while' loop is commonly used in real automation scripts for tasks like repeatedly checking 'while a specific file hasn't finished downloading yet' or 'while a user hasn't entered valid input', since the exact number of iterations needed isn't known in advance — this pattern is extremely common in Python-based web scraping and automation tools.

Without loops, repeating any action multiple times would require manually duplicating the same code for each repetition, which becomes impractical when processing large or variable-sized collections of data, like thousands of rows in a spreadsheet or an unknown number of lines in a log file. Python's loops allow scripts to process collections of data, repeat calculations, and automate repetitive tasks efficiently, and Python's 'for' loop in particular is specifically designed to iterate directly over the actual items of any iterable object, making common tasks like processing a list of items exceptionally clean and readable compared to manually managing an index counter.

  • for Loop (Iterating Over a Sequence): Python's for loop directly iterates over the elements of any iterable object (a list, tuple, string, dictionary, or range), automatically handling the underlying iteration mechanics without requiring a manually managed index counter.
  • for Loop with range(): Uses the built-in 'range()' function to generate a sequence of numbers, commonly used when a specific number of repetitions is needed (similar to a traditional counting for loop in other languages).
  • while Loop: Checks its condition before executing the loop body each time (entry-controlled), continuing to repeat the loop body as long as the condition remains True, used when the number of iterations isn't known in advance and depends on a dynamic condition.
  • Loop with an else Clause: A Python-specific feature where both 'for' and 'while' loops can have an optional 'else' block, which executes only if the loop completes fully WITHOUT being interrupted by a 'break' statement.
# for loop over an iterable: for item in iterable: # code block # for loop with range(): for i in range(start, stop, step): # code block # while loop: while condition: # code block
A beginner wants to print each name from a list of students, calculate the sum of numbers from 1 to 5 using range(), and simulate a simple retry mechanism using a while loop that keeps attempting an action until it succeeds or a maximum number of attempts is reached.
for Loop: Iterating Directly Over a List
This example demonstrates Python's idiomatic for loop, directly iterating over each element of a list without needing a separate index counter.
Python
students = ["Alice", "Bob", "Charlie"] for student in students: print("Hello, " + student + "!")
Hello, Alice! Hello, Bob! Hello, Charlie!
The loop reads naturally as 'for each student in the students list'. Python directly hands each individual element of the list to the 'student' variable on each iteration, automatically handling the underlying indexing internally — there is no need to manage a counter variable or check bounds manually, which is a core reason Python's for loop feels so different (and often simpler) compared to Java's traditional indexed for loop.
for Loop with range() for Counted Repetition
This example uses the built-in range() function to generate a sequence of numbers, calculating the sum of numbers from 1 to 5, demonstrating a counting-style loop.
Python
total = 0 for i in range(1, 6): total += i print("Sum of 1 to 5:", total)
Sum of 1 to 5: 15
'range(1, 6)' generates a sequence of integers starting at 1 (inclusive) and stopping just before 6 (exclusive), producing 1, 2, 3, 4, 5. The for loop iterates through each of these generated numbers, accumulating their total in the 'total' variable using the '+=' compound assignment operator. Note that range()'s stop value is always EXCLUSIVE, a common point of confusion for beginners expecting it to be inclusive.
while Loop: Simulating a Retry Mechanism
This example uses a while loop to simulate repeatedly attempting an action up to a maximum number of times, demonstrating a scenario where the exact number of iterations isn't known in advance.
Python
attempts = 0 max_attempts = 3 success = False while attempts < max_attempts and not success: attempts += 1 print("Attempt number:", attempts) if attempts == 2: success = True print("Final result - Success:", success, "| Total attempts:", attempts)
Attempt number: 1 Attempt number: 2 Final result - Success: True | Total attempts: 2
The while loop's condition, 'attempts < max_attempts and not success', is checked before every iteration; the loop continues only while BOTH the attempt limit hasn't been reached AND success hasn't yet occurred. On the second attempt, 'success' is set to True (simulating a successful action), which causes the condition to become False on the next check, correctly stopping the loop early at 2 attempts rather than running the full 3 allowed attempts.
  • Assuming range(5) Includes the Number 5: Writing 'for i in range(5):' and expecting it to include the number 5 is a common misunderstanding — 'range(5)' actually generates 0, 1, 2, 3, 4 (five numbers total, but stopping just before 5), since range()'s stop argument is always exclusive. Beginners expecting inclusive behavior are often surprised their loop runs one fewer iteration than anticipated.
  • Creating an Infinite Loop by Forgetting to Update the Condition Variable: Writing a while loop like 'while count < 10: print(count)' without ever incrementing 'count' anywhere inside the loop body causes the condition to remain True forever, resulting in an infinite loop that must be manually interrupted (e.g., with Ctrl+C in a terminal), since 'count' never actually changes to eventually make the condition False.
  • Modifying a List While Iterating Over It Directly with a for Loop: Directly calling 'my_list.remove(item)' inside a 'for item in my_list:' loop can cause elements to be unexpectedly skipped, since Python's list iteration relies on tracking the current index internally, and removing an element shifts all subsequent elements' positions, silently causing the loop to skip over what is now the 'next' element. A safe approach is to iterate over a COPY of the list (e.g., 'for item in my_list[:]:') or build a new filtered list instead.
  • Confusing the Purpose of range()'s Three Arguments: Beginners sometimes forget that 'range()' can take up to three arguments — start, stop, and step — and mistakenly assume 'range(10)' always starts from a specific number other than 0, or forget that a negative or custom step value (like 'range(10, 0, -1)' for counting backward) requires explicitly providing all three arguments, not just the stop value.
  • Prefer Direct Iteration Over Manual Indexing: When you only need each element's VALUE (not its position), iterate directly over the collection using 'for item in collection:' rather than manually looping with 'for i in range(len(collection)): item = collection[i]', since direct iteration is more concise, readable, and considered idiomatic ('Pythonic') code.
  • Use enumerate() When Both Index and Value Are Needed: When you need both an element's position AND its value during iteration (e.g., to print 'Item #1, Item #2'), use the built-in 'enumerate(collection)' function (e.g., 'for index, value in enumerate(my_list):') instead of manually managing a separate counter variable, since enumerate() provides both pieces of information cleanly in a single, readable loop.
  • Use while Loops Only When the Iteration Count Is Genuinely Unknown: Reserve 'while' loops specifically for situations where the number of iterations truly isn't known in advance (like waiting for a condition to become true, or processing input until a sentinel value is entered), rather than using a while loop with a manual counter to simulate what a simpler 'for' loop with range() could accomplish more cleanly.
  • Iterate Over a Copy When Modifying a List During Iteration: If elements genuinely need to be added or removed from a list while iterating over it, always iterate over an explicit copy of the list (using slicing, e.g., 'for item in my_list[:]:') to avoid the skipped-element bug, or better yet, build an entirely new list containing only the desired elements instead of modifying the original list in place during iteration.
How does Python's for loop differ fundamentally from a traditional counting for loop found in languages like Java or C?
Python's 'for' loop is designed to iterate directly over the elements of any ITERABLE object (like a list, tuple, string, dictionary, or the sequence generated by 'range()'), automatically handling the underlying mechanics of moving from one element to the next internally, without the programmer needing to manually declare, increment, or bounds-check an index counter variable. In contrast, a traditional Java or C-style for loop is fundamentally counter-based, explicitly requiring the programmer to declare an initial counter value, a continuation condition, and an increment/decrement expression as three separate parts of the loop's own syntax. To achieve a similar counting-style loop in Python, the built-in 'range()' function is typically used together with a for loop (e.g., 'for i in range(10):'), but Python's for loop itself is fundamentally an item-iteration construct, not a counter-based one, unlike its counterparts in many other mainstream languages.
What does Python's range() function's 'stop' argument actually represent, and why is this a common source of off-by-one confusion for beginners?
The 'stop' argument passed to 'range()' represents the value at which the generated sequence STOPS, but this value itself is always EXCLUDED from the actual generated sequence — 'range(5)' generates the numbers 0, 1, 2, 3, 4, deliberately stopping just before reaching 5. This is a common source of off-by-one confusion for beginners, who sometimes expect 'range(5)' to include the number 5 itself, or assume it starts counting from 1 rather than the actual default starting value of 0 (when only a single 'stop' argument is provided). This design choice actually aligns well with Python's own zero-based indexing convention for sequences — 'range(len(my_list))' conveniently generates exactly the valid index positions (0 through length-1) for that list, without needing any additional adjustment.
What is the purpose of the optional 'else' clause on a Python for or while loop, and under what specific condition does it NOT execute?
Python uniquely allows both 'for' and 'while' loops to have an optional 'else' block attached, which executes exactly once, but ONLY if the loop completes its ENTIRE iteration naturally, without ever being interrupted by a 'break' statement partway through. If a 'break' statement IS executed at any point during the loop (typically used to exit early once some specific condition or item is found), the loop's 'else' block is explicitly SKIPPED entirely, even though the loop itself has technically ended. This feature is most commonly used for search-like patterns — for example, looping through a list looking for a specific item, using 'break' immediately once it's found; the loop's 'else' clause can then be used to cleanly execute 'item not found' logic, which only makes sense to run if the loop finished searching through every single element without ever finding a match (i.e., without ever hitting the break).
Write a Python program using a for loop with range() that prints all even numbers from 2 to 20 (inclusive).
for num in range(2, 21, 2): print(num) # Output: # 2 # 4 # 6 # 8 # 10 # 12 # 14 # 16 # 18 # 20 # range(2, 21, 2) starts at 2, stops before 21 (so it includes up to 20), and steps by 2 each time, generating only even numbers directly without needing an if-check for evenness inside the loop.
What will be the output of the following code, and identify if it has any issue? count = 1 while count < 5: print("Count is:", count)
This code creates an infinite loop, continuously printing 'Count is: 1' forever, and will never terminate on its own. The issue is that 'count' is never updated (incremented) anywhere inside the loop body, so the condition 'count < 5' remains True indefinitely, since 'count' always stays equal to 1. To fix it, an increment statement like 'count += 1' must be added inside the loop body, for example after the print statement, so the condition eventually becomes False and the loop terminates correctly.
Using enumerate(), write a Python program that prints each fruit in a list along with its position number (starting from 1, not 0) for the list ['apple', 'banana', 'cherry'].
fruits = ['apple', 'banana', 'cherry'] for index, fruit in enumerate(fruits, start=1): print(str(index) + ". " + fruit) # Output: # 1. apple # 2. banana # 3. cherry # The 'start=1' argument to enumerate() shifts the generated index numbering to begin at 1 instead of the default 0, while 'fruit' still correctly receives each actual list element on every iteration.

Python's for loop is designed to iterate directly over the elements of any iterable object, making it fundamentally different from — and often simpler than — the counter-based for loops found in many other languages, while the while loop handles scenarios where the number of iterations depends on a dynamic condition rather than a known count. Understanding that range()'s stop value is always exclusive, using enumerate() when both index and value are needed, and being aware of pitfalls like infinite loops and modifying a list during iteration are essential skills for writing clean, correct, idiomatic Python repetition logic.

© 2026 CompileX. Maintained by Aditya Kumar Sharma.