Python Loops: for, while, nested
Python Loops Interview Questions
What are loops in Python and why are they used?
Loops are used to repeatedly execute a block of code. They automate repetitive tasks, process collections of data, and implement iterative algorithms. Python has two main loop types: for loops and while loops.
What is the basic syntax of a for loop in Python?
# Basic syntax
for item in iterable:
# code to execute for each item
# Examples
for i in range(5): # 0, 1, 2, 3, 4
print(i)
for char in "Hello": # H, e, l, l, o
print(char)
for num in [1, 2, 3, 4, 5]: # 1, 2, 3, 4, 5
print(num * 2)
The for loop iterates over each item in an iterable (list, string, tuple, dictionary, set, or range).
What is the basic syntax of a while loop in Python?
# Basic syntax
while condition:
# code to execute while condition is True
# Example: Count from 1 to 5
count = 1
while count <= 5:
print(count)
count += 1 # Important: Update the condition variable
# Example: User input until valid
password = ""
while password != "secret":
password = input("Enter password: ")
The while loop continues executing as long as the condition remains True.
What is the difference between for loop and while loop?
| Aspect | for loop | while loop |
|---|---|---|
| When to use | When you know number of iterations | When iterations depend on condition |
| Initialization | Automatic (gets next item) | Manual (before loop) |
| Condition | Implied (items in iterable) | Explicit (boolean expression) |
| Increment | Automatic (next iteration) | Manual (inside loop) |
| Risk | No infinite loop (finite iterable) | Risk of infinite loop |
What are nested loops in Python?
# Nested for loops
for i in range(3): # Outer loop
for j in range(2): # Inner loop
print(f"({i}, {j})")
# Output: (0,0), (0,1), (1,0), (1,1), (2,0), (2,1)
# Nested while loops
i = 1
while i <= 3:
j = 1
while j <= 2:
print(f"i={i}, j={j}")
j += 1
i += 1
# Mixed nesting
for row in range(3):
col = 0
while col < 2:
print(f"[{row},{col}]")
col += 1
A loop inside another loop. The inner loop completes all iterations for each iteration of the outer loop.
What is the range() function and how is it used with for loops?
# range(stop) - 0 to stop-1
for i in range(5): # 0, 1, 2, 3, 4
print(i)
# range(start, stop) - start to stop-1
for i in range(2, 6): # 2, 3, 4, 5
print(i)
# range(start, stop, step) - with step
for i in range(1, 10, 2): # 1, 3, 5, 7, 9
print(i)
# Negative step
for i in range(5, 0, -1): # 5, 4, 3, 2, 1
print(i)
# Common patterns
for i in range(len(my_list)): # Index-based iteration
print(f"Index {i}: {my_list[i]}")
for i in range(10, 0, -1): # Countdown
print(i)
print("Blast off!")
What are loop control statements (break, continue, pass)?
# break - exit the loop entirely
for i in range(10):
if i == 5:
break # Loop stops at i=5
print(i) # Output: 0, 1, 2, 3, 4
# continue - skip current iteration, continue with next
for i in range(5):
if i == 2:
continue # Skip i=2
print(i) # Output: 0, 1, 3, 4
# pass - do nothing (placeholder)
for i in range(5):
if i == 3:
pass # Nothing happens
print(i) # Output: 0, 1, 2, 3, 4
# else with loops - executes when loop completes normally (no break)
for i in range(5):
print(i)
else:
print("Loop completed successfully")
What is an infinite loop and how to create/avoid it?
# Infinite while loop (condition always True)
# while True:
# print("This runs forever!")
# Common infinite loop patterns
# count = 1
# while count < 5: # Forgot to increment count
# print(count) # Infinite loop!
# How to avoid infinite loops
count = 1
while count < 5:
print(count)
count += 1 # Always update condition variable
# Using break to exit infinite loop
while True:
user_input = input("Enter 'quit' to exit: ")
if user_input == 'quit':
break
print(f"You entered: {user_input}")
# Safe pattern with timeout
import time
start_time = time.time()
max_time = 10 # seconds
while True:
# Process something
if time.time() - start_time > max_time:
print("Timeout!")
break
How to iterate over dictionaries with for loops?
my_dict = {'name': 'Alice', 'age': 30, 'city': 'New York'}
# Iterate over keys (default)
for key in my_dict:
print(f"Key: {key}")
# Explicitly iterate over keys
for key in my_dict.keys():
print(f"Key: {key}")
# Iterate over values
for value in my_dict.values():
print(f"Value: {value}")
# Iterate over key-value pairs
for key, value in my_dict.items():
print(f"{key}: {value}")
# Practical example
student_grades = {'John': 85, 'Emma': 92, 'Mike': 78}
for name, grade in student_grades.items():
if grade >= 90:
print(f"{name}: A")
elif grade >= 80:
print(f"{name}: B")
What is enumerate() and how is it used with for loops?
# enumerate() adds counter to iterable
fruits = ['apple', 'banana', 'cherry']
# Without enumerate
index = 0
for fruit in fruits:
print(f"{index}: {fruit}")
index += 1
# With enumerate (much cleaner!)
for index, fruit in enumerate(fruits):
print(f"{index}: {fruit}")
# With custom start index
for index, fruit in enumerate(fruits, start=1):
print(f"{index}. {fruit}")
# Practical example
students = ['Alice', 'Bob', 'Charlie']
for position, student in enumerate(students, start=1):
print(f"Position {position}: {student}")
# Enumerate with condition
for i, num in enumerate([10, 20, 30, 40, 50]):
if num > 25:
print(f"Index {i}: {num} is greater than 25")
What is zip() and how is it used with for loops?
# zip() combines multiple iterables
names = ['Alice', 'Bob', 'Charlie']
ages = [25, 30, 35]
cities = ['NYC', 'LA', 'Chicago']
# Iterate over multiple lists simultaneously
for name, age, city in zip(names, ages, cities):
print(f"{name} is {age} years old and lives in {city}")
# With enumerate and zip
for i, (name, age) in enumerate(zip(names, ages), start=1):
print(f"{i}. {name}: {age} years")
# Different length lists - stops at shortest
list1 = [1, 2, 3]
list2 = ['a', 'b']
for num, letter in zip(list1, list2):
print(f"{num}{letter}") # Output: 1a, 2b (3 is skipped)
# To handle different lengths, use itertools.zip_longest
import itertools
for num, letter in itertools.zip_longest(list1, list2, fillvalue='-'):
print(f"{num}{letter}") # Output: 1a, 2b, 3-
How to use else clause with loops?
# else executes when loop completes normally (no break)
# For loop with else
for i in range(5):
print(i)
else:
print("Loop completed without break")
# With break - else won't execute
for i in range(5):
if i == 3:
break
print(i)
else:
print("This won't print because of break")
# While loop with else
count = 0
while count < 3:
print(count)
count += 1
else:
print("While loop completed")
# Practical example: Search with else
numbers = [1, 3, 5, 7, 9]
search_for = 6
for num in numbers:
if num == search_for:
print(f"Found {search_for}")
break
else:
print(f"{search_for} not found in list") # This executes
What are list comprehensions and how do they relate to loops?
# List comprehension - concise way to create lists
# Traditional for loop
squares = []
for i in range(5):
squares.append(i**2)
print(squares) # [0, 1, 4, 9, 16]
# List comprehension equivalent
squares = [i**2 for i in range(5)]
print(squares) # [0, 1, 4, 9, 16]
# With condition (if)
even_squares = [i**2 for i in range(10) if i % 2 == 0]
print(even_squares) # [0, 4, 16, 36, 64]
# Nested loops in comprehension
pairs = [(x, y) for x in range(3) for y in range(2)]
print(pairs) # [(0,0), (0,1), (1,0), (1,1), (2,0), (2,1)]
# Dictionary comprehension
square_dict = {x: x**2 for x in range(5)}
print(square_dict) # {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}
# Set comprehension
unique_squares = {x**2 for x in range(-5, 6)}
print(unique_squares) # {0, 1, 4, 9, 16, 25}
How to iterate over multiple sequences simultaneously?
# Method 1: Using zip() (most common)
names = ['Alice', 'Bob', 'Charlie']
ages = [25, 30, 35]
for name, age in zip(names, ages):
print(f"{name} is {age} years old")
# Method 2: Using range() with index
for i in range(len(names)):
print(f"{names[i]} is {ages[i]} years old")
# Method 3: Using enumerate() with zip
for i, (name, age) in enumerate(zip(names, ages)):
print(f"{i+1}. {name}: {age}")
# Method 4: Using itertools.zip_longest for different lengths
import itertools
list1 = [1, 2, 3]
list2 = ['a', 'b']
for a, b in itertools.zip_longest(list1, list2, fillvalue='N/A'):
print(f"{a} - {b}") # 1-a, 2-b, 3-N/A
# Method 5: Manual index with while loop
i = 0
while i < len(names) and i < len(ages):
print(f"{names[i]}: {ages[i]}")
i += 1
What are common loop patterns and idioms?
# 1. Accumulator pattern
total = 0
for num in [1, 2, 3, 4, 5]:
total += num
print(f"Total: {total}")
# 2. Counter pattern
count = 0
for char in "hello world":
if char == 'l':
count += 1
print(f"Count of 'l': {count}")
# 3. Search pattern
found = False
for item in [1, 3, 5, 7, 9]:
if item == 5:
found = True
break
print(f"Found 5: {found}")
# 4. Filter pattern
evens = []
for num in range(10):
if num % 2 == 0:
evens.append(num)
print(f"Even numbers: {evens}")
# 5. Transform pattern
squared = []
for num in range(5):
squared.append(num ** 2)
print(f"Squares: {squared}")
# 6. Nested loop for matrix
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
for row in matrix:
for element in row:
print(element, end=' ')
print() # New line after each row
How to avoid modifying a list while iterating over it?
# Problem: Modifying list while iterating
numbers = [1, 2, 3, 4, 5]
# for num in numbers:
# if num % 2 == 0:
# numbers.remove(num) # This causes issues!
# Solution 1: Create a copy
numbers = [1, 2, 3, 4, 5]
for num in numbers.copy(): # or numbers[:]
if num % 2 == 0:
numbers.remove(num)
print(numbers) # [1, 3, 5]
# Solution 2: Use list comprehension (create new list)
numbers = [1, 2, 3, 4, 5]
numbers = [num for num in numbers if num % 2 != 0]
print(numbers) # [1, 3, 5]
# Solution 3: Iterate over indices in reverse
numbers = [1, 2, 3, 4, 5]
for i in range(len(numbers)-1, -1, -1):
if numbers[i] % 2 == 0:
numbers.pop(i)
print(numbers) # [1, 3, 5]
# Solution 4: Collect items to remove, then remove them
numbers = [1, 2, 3, 4, 5]
to_remove = []
for num in numbers:
if num % 2 == 0:
to_remove.append(num)
for item in to_remove:
numbers.remove(item)
print(numbers) # [1, 3, 5]
What is the difference between iterating over a list vs iterating over its indices?
fruits = ['apple', 'banana', 'cherry']
# Method 1: Iterate over elements (Pythonic)
for fruit in fruits:
print(fruit)
# Output: apple, banana, cherry
# Method 2: Iterate over indices
for i in range(len(fruits)):
print(f"{i}: {fruits[i]}")
# Output: 0: apple, 1: banana, 2: cherry
# When to use indices:
# 1. Need index for something else
for i in range(len(fruits)):
print(f"Index {i}: {fruits[i]}")
# 2. Modifying elements
for i in range(len(fruits)):
fruits[i] = fruits[i].upper()
print(fruits) # ['APPLE', 'BANANA', 'CHERRY']
# 3. Accessing multiple lists at same index
names = ['Alice', 'Bob', 'Charlie']
ages = [25, 30, 35]
for i in range(len(names)):
print(f"{names[i]} is {ages[i]} years old")
# Better: Use enumerate()
for i, fruit in enumerate(fruits):
print(f"{i}: {fruit}")
How to use loops with files?
# Reading a file line by line
with open('data.txt', 'r') as file:
for line in file:
print(line.strip()) # strip() removes newline
# With line numbers
with open('data.txt', 'r') as file:
for line_num, line in enumerate(file, start=1):
print(f"{line_num}: {line.strip()}")
# Reading specific number of lines
with open('data.txt', 'r') as file:
for i in range(5): # Read first 5 lines
line = file.readline()
if not line: # End of file
break
print(line.strip())
# Processing CSV data
import csv
with open('data.csv', 'r') as file:
csv_reader = csv.reader(file)
for row in csv_reader:
# Process each row (list of values)
print(row)
# Writing to file in a loop
data = ['Line 1', 'Line 2', 'Line 3']
with open('output.txt', 'w') as file:
for line in data:
file.write(line + '\n')
What are generator expressions and how do they relate to loops?
# Generator expression - memory efficient alternative to list comprehension
# List comprehension (creates full list in memory)
squares_list = [x**2 for x in range(1000000)] # Uses lots of memory
# Generator expression (creates generator object)
squares_gen = (x**2 for x in range(1000000)) # Memory efficient
# Using generator in loop
for square in (x**2 for x in range(5)):
print(square)
# With condition
even_squares = (x**2 for x in range(10) if x % 2 == 0)
for num in even_squares:
print(num)
# Converting generator to list
gen = (x for x in range(5))
my_list = list(gen) # [0, 1, 2, 3, 4]
# Generator with sum() - no intermediate list
total = sum(x**2 for x in range(1000)) # Memory efficient
# Compare memory usage
import sys
list_comp = [x**2 for x in range(1000)]
gen_exp = (x**2 for x in range(1000))
print(f"List size: {sys.getsizeof(list_comp)} bytes")
print(f"Generator size: {sys.getsizeof(gen_exp)} bytes")
What are common loop mistakes and how to avoid them?
# 1. Infinite loop (forgot to update condition)
# count = 0
# while count < 5:
# print(count) # Forgot count += 1
# 2. Modifying list while iterating
# items = [1, 2, 3, 4]
# for item in items:
# items.remove(item) # Unexpected behavior
# 3. Using same variable name in nested loops
for i in range(3):
for i in range(2): # Reusing variable name
print(i) # Inner loop overwrites i
# 4. Off-by-one errors with range()
for i in range(5): # 0 to 4 (not 1 to 5)
print(i)
# 5. Forgetting to convert range to list
r = range(5)
print(r) # range(0, 5) - not a list!
print(list(r)) # [0, 1, 2, 3, 4]
# 6. Using loop variable after loop
for i in range(5):
pass
print(i) # i = 4 (last value) - can be confusing
# 7. Not using enumerate when index is needed
fruits = ['apple', 'banana']
# Instead of:
for i in range(len(fruits)):
print(f"{i}: {fruits[i]}")
# Use:
for i, fruit in enumerate(fruits):
print(f"{i}: {fruit}")
Note: These questions cover Python loops comprehensively. Remember: Use for loops when you know the number of iterations, and while loops when iterations depend on a condition. Always be cautious of infinite loops. Python offers many elegant loop constructs like list comprehensions, generator expressions, enumerate(), and zip() that can make your code more readable and efficient.