Previous | Tutorial index | Next
Move beyond basic syntax and learn the recurring "design patterns" of loops. You will learn how to solve common problems elegantly, use Python's built-in tools for cleaner code, and adopt professional best practices to write maintainable, bug-free loops.
When you write code, you'll notice that certain problems appear over and over again. Instead of reinventing the wheel each time, you can recognize these patterns and apply a standardized solution.
Think of a chef in a kitchen. They don't invent a new way to chop an onion every timeβthey have a pattern (the knife technique) that they apply whenever they see an onion. Similarly, programmers have patterns for:
Mastering these patterns makes you a faster, more reliable programmer.
The accumulation pattern involves starting with a "blank slate" variable and gradually adding to it during each loop iteration.
total = 0 # The accumulator
for i in range(1, 11):
total = total + i # Or total += i
print(total) # Output: 55
(Note: Product accumulators always start at 1, not 0, because multiplying by 0 ruins everything!)
factorial = 1
for i in range(1, 6): # 5! = 1*2*3*4*5 = 120
factorial *= i
print(factorial) # Output: 120
How many vowels are in a string?
text = "Hello World"
vowel_count = 0
for char in text:
if char.lower() in "aeiou":
vowel_count += 1
print(vowel_count) # Output: 3 (e, o, o)
words = ["Python", "is", "powerful"]
sentence = ""
for word in words:
sentence += word + " "
print(sentence.strip()) # Output: "Python is powerful"
β οΈ Pro-Tip: While += works for small strings, it is inefficient for large ones (it creates new strings repeatedly). For large-scale string building, use " ".join(words) or collect items in a list and join at the end.
Searching involves looking through a collection for the first item that meets a specific condition. Once found, you usually want to stop immediately (using break).
# Find the first number greater than 50
numbers = [10, 30, 60, 80, 20]
target = None # Sentinel value to indicate "not found"
for num in numbers:
if num > 50:
target = num
break # Exit early! We found it.
if target is not None:
print(f"Found: {target}")
else:
print("No number greater than 50.")
The Search with Index: Sometimes you need to know where in the list the item is.
fruits = ["apple", "banana", "cherry", "durian"]
search_item = "cherry"
found_index = -1 # -1 is a common sentinel for "not found"
for i in range(len(fruits)):
if fruits[i] == search_item:
found_index = i
break
if found_index != -1:
print(f"Found '{search_item}' at index {found_index}")
else:
print("Not found.")
The flag pattern uses a Boolean variable (True / False) to track whether a specific condition occurred at any point during the loop. This is very common for data validation.
Example: Are there any negative numbers in this list?
numbers = [5, 12, -3, 8, 0]
has_negative = False # The flag starts as False
for num in numbers:
if num < 0:
has_negative = True
break # We know the answer now, so we can stop!
if has_negative:
print("Warning: Negative number found!")
else:
print("All numbers are non-negative.")
Example: Checking if a number is Prime (using a flag).
n = 29
is_prime = True # Assume it's prime until proven otherwise
if n < 2:
is_prime = False
else:
for i in range(2, int(n**0.5) + 1):
if n % i == 0:
is_prime = False
break
print(f"{n} is prime: {is_prime}")
Filtering involves iterating over a collection and building a new collection containing only the elements that pass a certain test.
# Keep only the even numbers
original = [1, 2, 3, 4, 5, 6, 7, 8]
evens = []
for num in original:
if num % 2 == 0:
evens.append(num)
print(evens) # Output: [2, 4, 6, 8]
(Note: In professional Python, we use List Comprehensions [num for num in original if num % 2 == 0], but the loop pattern is the foundation.)
enumerate() and zip()Python provides built-in functions that make loops cleaner, more readable, and more powerful. These are considered "Pythonic" (idiomatic to the language).
enumerate() FunctionIf you need the index and the value simultaneously while looping, enumerate() is your best friend. It returns a tuple (index, value).
colors = ["red", "green", "blue"]
# Without enumerate (clunky)
for i in range(len(colors)):
print(f"{i}: {colors[i]}")
# With enumerate (clean and Pythonic)
for index, color in enumerate(colors):
print(f"{index}: {color}")
Bonus: You can start counting from a different number.
for index, color in enumerate(colors, start=1):
print(f"Color #{index}: {color}")
Output: Color #1: red, Color #2: green, etc.
zip() FunctionIf you need to loop over two or more lists in parallel, use zip(). It pairs up the elements at the same positions.
names = ["Alice", "Bob", "Charlie"]
scores = [85, 92, 78]
for name, score in zip(names, scores):
print(f"{name} scored {score}%")
Output:
Alice scored 85%
Bob scored 92%
Charlie scored 78%
How zip() stops: It stops when the shortest input list ends.
a = [1, 2, 3]
b = [10, 20]
for x, y in zip(a, b):
print(x, y) # Output: (1,10) and (2,20) -- 3 is ignored!
Advanced Use: Zipping three or more lists.
first = ["A", "B"]
second = ["C", "D"]
third = ["E", "F"]
for x, y, z in zip(first, second, third):
print(x, y, z) # Output: A C E, B D F
A loop body should do one thing well. If your loop is 20 lines long, it's time to refactor. Move complex logic into a function outside the loop.
Bad:
for item in big_list:
# 20 lines of complex validation, calculations, and file writing here.
pass
Good:
def process_item(item):
# All the complex logic lives here.
return result
for item in big_list:
result = process_item(item)
# Just one line in the loop.
Don't write cryptic conditions. Use meaningful variable names.
Bad: while x > 0 and not y == 3 and z < 10:
Good:
while remaining_items > 0 and not is_error_state and current_depth < max_depth:
This is the classic "unexpected behavior" trap. If you remove or add items to a list while a for loop is iterating over it, the internal index counter gets confused, and you will skip items or get IndexError.
β DANGEROUS CODE (Skipping items):
numbers = [1, 2, 3, 4, 5]
for num in numbers:
if num % 2 == 1: # If odd
numbers.remove(num) # Remove it
print(numbers) # Output: [2, 4] -- Wait, where is 3 and 5? They got skipped!
(Why? When you remove 1, the list shifts. The loop moves to index 1, which is now 3 (old index 2), so 2 is skipped!)
β SAFE FIX #1: Iterate over a COPY.
numbers = [1, 2, 3, 4, 5]
for num in numbers[:]: # The [:] creates a copy of the list.
if num % 2 == 1:
numbers.remove(num)
print(numbers) # Output: [2, 4]
β SAFE FIX #2: Build a New List (Filtering).
numbers = [1, 2, 3, 4, 5]
evens = []
for num in numbers:
if num % 2 == 0:
evens.append(num)
numbers = evens
print(numbers) # Output: [2, 4]
else with Loops for Search PatternWe covered this in Tutorial 3, but it bears repeating. The else clause runs only if the loop completes without a break. It is perfect for search patterns.
for num in [10, 20, 30]:
if num == 15:
print("Found!")
break
else:
print("15 was not found.")
Q1: What is the value of total after this code runs?
total = 1
for i in range(1, 5):
total *= i
print(total)
a) 10
b) 24
c) 0
Q2: What does the following code print?
names = ["Anna", "Bob", "Charlie"]
for i, name in enumerate(names, start=10):
print(i, end=" ")
a) 0 1 2
b) 10 11 12
c) Anna Bob Charlie
Q3: What is the output of this code?
list1 = [1, 2, 3]
list2 = ['a', 'b']
for x, y in zip(list1, list2):
print(x, y)
a) (1,a), (2,b), (3, None)
b) (1,a), (2,b)
c) Error because lists have different lengths.
Q4: (True/False) It is perfectly safe to delete items from a list while iterating over it using for item in my_list:.
Q5: Which pattern uses a boolean variable that starts as False and is set to True if something happens?
a) Accumulation
b) Flag
c) Zip
Exercise 1 (Accumulation - Average):
Write a program that asks the user to enter 5 numbers (use a for loop with range(5)). Accumulate their sum. After the loop, print the average (sum / 5).
total = 0
for _ in range(5):
total += float(input("Enter number: "))
print(total/5)
Exercise 2 (Searching with enumerate):
Given grades = [45, 67, 88, 52, 94], write a program that finds the first failing grade (below 60) and prints its index and value. Use enumerate() and break.
grades = [45,67,88,52,94]
for idx, g in enumerate(grades):
if g < 60:
print(f"First failing: {g} at index {idx}")
break
Exercise 3 (Flag - All Positive): Write a program that asks the user to enter 3 numbers. Use a flag to check if all numbers are positive. If any number is zero or negative, print "Not all positive". Otherwise, print "All positive!".
all_pos = True
for _ in range(3):
if float(input()) <= 0:
all_pos = False
print("All positive" if all_pos else "Not all positive")
Exercise 4 (Zip - Matches Counter):
Given two lists: correct_answers = ["A", "B", "C", "D"] and student_answers = ["A", "B", "D", "D"]. Use zip() to count how many answers the student got correct.
correct = ["A","B","C","D"]
student = ["A","B","D","D"]
score = sum(1 for c,s in zip(correct, student) if c==s)
print(f"Score: {score}/4")
Exercise 5 (Filtering with Copy):
Given nums = [10, 21, 30, 43, 50, 61], write a loop that removes all odd numbers using a copy of the list (nums[:]).
details>
nums = [10,21,30,43,50,61]
for n in nums[:]:
if n % 2 == 1:
nums.remove(n)
print(nums)
Question 1: The GPA Calculator (Accumulation + Zip)
You are given two lists: credits = [3, 4, 3, 2] and grade_points = [4.0, 3.0, 3.7, 2.0] (where A=4.0, B=3.0, etc.).
zip() to calculate the total grade points earned (credit * grade points) for each course.credits = [3,4,3,2]
grade_points = [4.0,3.0,3.7,2.0]
total_points = 0
total_credits = 0
for c, g in zip(credits, grade_points):
total_points += c * g
total_credits += c
print(f"GPA: {total_points/total_credits:.2f}")
Question 2: The Duplicate Detector (Flag Pattern)
Write a program that takes a list (e.g., [1, 2, 3, 4, 2, 5]) and uses nested loops (a flag pattern) to check if there are any duplicate numbers. The flag should be has_duplicate. Print "Duplicates found" or "No duplicates" accordingly.
(Hint: Compare list[i] with list[j] where i and j are different indices. Use break to optimize).
lst = [1,2,3,4,2,5]
has_dup = False
for i in range(len(lst)):
for j in range(i+1, len(lst)):
if lst[i] == lst[j]:
has_dup = True
break
if has_dup:
break
print("Duplicates found" if has_dup else "No duplicates")
Question 3: The Student Roster (Enumerate + Filtering)
You have a list of student names: students = ["Alice", "Bob", "Charlie", "David", "Eve"]. You need to create a new list selected containing students whose index is even (0, 2, 4). Use enumerate() to check the index, and if the index is even, add the student to the new list. Print the selected list.
students = ["Alice","Bob","Charlie","David","Eve"]
selected = [name for idx, name in enumerate(students) if idx % 2 == 0]
print(selected)
Question 4: The Matrix Column Sum (Nested Loops + Accumulation)
Given a 2D matrix: matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]. Write a program that calculates the sum of each column (not row).
Expected Output: Column 0 sum: 12, Column 1 sum: 15, Column 2 sum: 18.
(Hint: The outer loop should iterate over columns, and the inner loop over rows).
matrix = [[1,2,3],[4,5,6],[7,8,9]]
for c in range(len(matrix[0])):
col_sum = sum(row[c] for row in matrix)
print(f"Column {c} sum: {col_sum}")
Question 5: The Event Logger (Combining Patterns)
You are given a list of tuples: logs = [("user1", "login"), ("user2", "error"), ("user1", "error"), ("user3", "login"), ("user2", "logout")].
Write a program that:
error_logs containing only the tuples where the event is "error". Print error_logs.logs = [("user1","login"), ("user2","error"), ("user1","error"), ("user3","login"), ("user2","logout")]
has_error = any(event == "error" for _, event in logs)
print("System has errors" if has_error else "System running smoothly")
error_logs = [(u,e) for u,e in logs if e == "error"]
print("Error logs:", error_logs)
# Count events per user
counts = {}
for user, _ in logs:
counts[user] = counts.get(user, 0) + 1
print("User event counts:", counts)
Congratulations! You have completed the final fundamental tutorial on loops. You now possess a toolkit of patterns (Accumulation, Searching, Flagging, Filtering) and Pythonic tools (enumerate, zip) that will allow you to tackle 80% of the looping problems you'll encounter in the real world. Combine this with the break/continue logic from Tutorial 3, and you are ready to move on to functions!