Airbnb's pricing engine uses zip to pair availability calendars with pricing calendars for millions of listings simultaneously, iterating both sequences in lockstep to compute dynamic rates without a single index variable. When engineers process two related data streams at the same time, zip turns a nested loop into a single clean iteration that is both faster and easier to read. The intermediate loop patterns in this lesson, including enumerate, zip, and break/continue, are the toolkit that separates readable production code from amateur scripts.
enumerate() for Index
Daily Life
Interviews
Track position and value together
The enumerate() function adds a counter to an iterable. It returns pairs of (index, value) on each iteration, giving you access to both the position and the item without manually managing an index variable.
Basic enumerate Usage
enumerate(iterable) returns an enumerate object that yields (index, item) tuples. You typically unpack these in the for statement:
1
fruits=["apple","banana","cherry"]
2
3
print("Manual approach:")
4
i=0
5
forfruitinfruits:
6
print(str(i)+": "+fruit)
7
i+=1
8
9
print()
10
11
print("With enumerate:")
12
forindex,fruitinenumerate(fruits):
13
print(str(index)+": "+fruit)
>>>Output
Manual approach:
0: apple
1: banana
2: cherry
With enumerate:
0: apple
1: banana
2: cherry
The index,fruit syntax unpacks the tuple returned by enumerate. The first value is the index, the second is the item from the list.
Starting at Any Index
By default, enumerate starts counting at 0. Use the start parameter to begin at a different number:
1
tasks=["Wake up","Eat breakfast","Go to work"]
2
3
# Start counting from 1 (more natural for display)
4
print("Daily tasks:")
5
fornum,taskinenumerate(tasks,start=1):
6
print(str(num)+". "+task)
7
8
print()
9
10
# Start from any number
11
print("Continue from 10:")
12
fori,taskinenumerate(tasks,start=10):
13
print("Task #"+str(i)+": "+task)
>>>Output
Daily tasks:
1. Wake up
2. Eat breakfast
3. Go to work
Continue from 10:
Task #10: Wake up
Task #11: Eat breakfast
Task #12: Go to work
enumerate() Patterns
enumerate is essential when you need to modify a list in place or when position matters:
Here are the most common scenarios where enumerate() saves you from manual index tracking:
Modify list elements in place
Use the index from enumerate to update items at their original position
Find indices of matches
Locate where specific values appear without a separate counter variable
Display progress indicators
Show the user how far along processing has reached in a large dataset
Create position-based mappings
Build dictionaries that map indices to values using enumerate pairs
zip() Parallel Iteration
Daily Life
Interviews
Iterate multiple lists in lockstep
The zip() function combines multiple iterables element-by-element. On each iteration, it yields a tuple containing one item from each input sequence. This lets you iterate over related data in parallel.
Basic zip Usage
zip(iterable1,iterable2,...) pairs up items from each iterable by position:
1
names=["Alice","Bob","Charlie"]
2
ages=[25,30,35]
3
4
# Iterate over both lists in parallel
5
forname,ageinzip(names,ages):
6
print(name+" is "+str(age)+" years old")
>>>Output
Alice is 25 years old
Bob is 30 years old
Charlie is 35 years old
On the first iteration, you get ("Alice", 25). On the second, ("Bob", 30). The items are paired by their position in each list.
zip() with Unequal Lengths
When sequences have different lengths, zip stops at the shortest one:
1
names=["Alice","Bob","Charlie","Diana"]
2
scores=[85,92,78]
3
4
# Only 3 pairs - stops at shortest sequence
5
forname,scoreinzip(names,scores):
6
print(name+":",score)
7
8
print()
9
10
# Extra items in longer list are ignored
11
letters=["a","b"]
12
numbers=[1,2,3,4,5]
13
forletter,numinzip(letters,numbers):
14
print(letter,num)
>>>Output
Alice: 85
Bob: 92
Charlie: 78
a 1
b 2
Knowing when to reach for enumerate versus zip versus plain iteration saves you from writing unnecessary boilerplate. Here is a concise guide.
Choosing the Right Iteration Tool
Need just values: plain for loop over the sequence.
Need index and value: enumerate() with tuple unpacking.
Need values from two or more lists in lockstep: zip().
Need index plus multiple lists: enumerate(zip(a, b)).
print("Position "+str(i)+" to "+str(i+1)+": "+direction+" "+str(abs(diff)))
>>>Output
Ranked results:
1. Alice: 85
2. Bob: 92
3. Charlie: 78
Consecutive differences:
Position 0 to 1: up 10
Position 1 to 2: down 5
Position 2 to 3: up 10
Position 3 to 4: up 5
Transposing with zip
A clever use of zip with the * unpacking operator transposes rows and columns in a matrix:
1
# Original matrix (3 rows, 4 columns)
2
matrix=[
3
[1,2,3,4],
4
[5,6,7,8],
5
[9,10,11,12]
6
]
7
8
print("Original:")
9
forrowinmatrix:
10
print(row)
11
12
# Transpose using zip(*matrix)
13
transposed=list(zip(*matrix))
14
15
print()
16
print("Transposed (4 rows, 3 columns):")
17
forrowintransposed:
18
print(list(row))
>>>Output
Original:
[1, 2, 3, 4]
[5, 6, 7, 8]
[9, 10, 11, 12]
Transposed (4 rows, 3 columns):
[1, 5, 9]
[2, 6, 10]
[3, 7, 11]
[4, 8, 12]
Python Quiz
> Pair names with their scores so each element is a tuple. Pick the built-in that combines two sequences element-by-element, and the one that counts the resulting pairs.
zip() is lazy: it does not build a list in memory until you ask for it. Wrap it in list() to materialize all pairs, or iterate directly in a for loop to process one pair at a time.
enumerate() and zip() cover the two most common iteration patterns: working with a single sequence with positional context, or working with multiple parallel sequences together.
When zipping sequences of different lengths, zip() stops at the shortest one. Use itertools.zip_longest() if you need to process all elements and fill missing values with a default.
Iterating Dict Items
Daily Life
Interviews
Loop through dict keys, values, or both
Dictionaries provide several methods for iteration: you can loop over just keys, just values, or key-value pairs together. Each approach is useful in different situations.
Iterating Over Keys
By default, iterating over a dictionary yields its keys:
1
person={"name":"Alice","age":25,"city":"NYC"}
2
3
# Default iteration gives keys
4
print("Keys:")
5
forkeyinperson:
6
print(key)
7
8
print()
9
10
# Explicitly using .keys() - same result
11
print("Using .keys():")
12
forkeyinperson.keys():
13
print(key)
>>>Output
Keys:
name
age
city
Using .keys():
name
age
city
Iterating Over Values
Use .values() to iterate over just the values:
1
scores={"Alice":85,"Bob":92,"Charlie":78}
2
3
# Iterate over values only
4
print("Scores:")
5
forscoreinscores.values():
6
print(score)
7
8
print()
9
10
# Calculate total
11
total=0
12
forscoreinscores.values():
13
total+=score
14
print("Total:",total)
15
print("Average:",total/len(scores))
>>>Output
Scores:
85
92
78
Total: 255
Average: 85.0
Iterating Key-Value Pairs
Use .items() to get both key and value together. This is the most commonly used pattern:
Users by role: {'admin': ['Alice', 'Charlie'], 'user': ['Bob', 'Diana']}
TIP
The pattern of checking if a key exists before updating is common. Python's collections.defaultdict and dict.setdefault() methods can simplify this, which you'll learn in more advanced lessons.
When iterating over dictionaries, follow these practices to keep your code safe and readable:
✓Do
Use .items() when you need both key and value
Use dict comprehension for filtering
Iterate over list(dict.keys()) to modify
Use descriptive names for k, v
✗Don't
Add or delete keys while iterating directly
Use dict[key] inside a for-key loop when .items() works
Assume insertion order in Python < 3.7
Modify values through a values() view
Python Quiz
> Sum the numeric values and collect the key names from a dictionary. Pick the accessor that returns just the numbers, and the one that returns just the names.
Choosing the right dictionary view method keeps your intent clear. Use .keys() when only the labels matter, .values() when only the data matters, and .items() when you need both together.
Dictionary views are live: if you add or remove keys after creating a view, the view reflects those changes immediately. Convert to a list if you need a stable snapshot of the keys or values.
The grouping pattern, where you check if a key exists before appending to its list, is one of the most common dictionary-building idioms in Python and appears frequently in data aggregation tasks.
Nested Loops
Daily Life
Interviews
Process grids and generate combinations
A nested loop is a loop inside another loop. The inner loop runs completely for each iteration of the outer loop. This pattern is essential for working with multi-dimensional data structures and generating combinations.
Basic Nested Loop
The inner loop completes all its iterations before the outer loop moves to the next item:
1
foriinrange(1,4):
2
print("Outer:",i)
3
forjinrange(1,3):
4
print(" Inner:",j)
5
print()
>>>Output
Outer: 1
Inner: 1
Inner: 2
Outer: 2
Inner: 1
Inner: 2
Outer: 3
Inner: 1
Inner: 2
The outer loop runs 3 times. For each outer iteration, the inner loop runs 2 times. Total iterations: 3 x 2 = 6.
Working with 2D Data
Nested loops are natural for processing matrices (lists of lists):
Nested loops naturally generate all combinations of elements:
1
colors=["red","blue"]
2
sizes=["S","M","L"]
3
4
# All color-size combinations
5
print("Products:")
6
forcolorincolors:
7
forsizeinsizes:
8
print(color+"-"+size)
9
10
print()
11
12
# Multiplication table
13
print("Multiplication table (1-3):")
14
foriinrange(1,4):
15
row=""
16
forjinrange(1,4):
17
row+=str(i*j)+" "
18
print(row)
>>>Output
Products:
red-S
red-M
red-L
blue-S
blue-M
blue-L
Multiplication table (1-3):
1 2 3
2 4 6
3 6 9
TIP
Nested loops multiply iterations. A loop of 1000 inside a loop of 1000 runs 1,000,000 times. Be mindful of performance with deeply nested loops over large datasets.
Triangle and Pyramid
Nested loops where the inner loop depends on the outer loop create triangle patterns. This is common for comparisons and hierarchical displays:
1
print("Triangle:")
2
foriinrange(1,6):
3
row=""
4
forjinrange(i):
5
row+="* "
6
print(row)
7
8
print()
9
10
# Pairs without repetition
11
items=["A","B","C","D"]
12
print("Unique pairs:")
13
foriinrange(len(items)):
14
forjinrange(i+1,len(items)):
15
print(items[i]+"-"+items[j])
>>>Output
Triangle:
*
* *
* * *
* * * *
* * * * *
Unique pairs:
A-B
A-C
A-D
B-C
B-D
C-D
In the unique pairs example, starting the inner loop at i+1 ensures each pair is only counted once (A-B but not B-A).
This nested loop has a bug that causes incorrect output. Can you find and remove the extra tile?
Debug Challenge
> This nested loop should print a multiplication table, but the formula is wrong. Remove the extra tile to fix it.
LogicError: printing i*i*j instead of i*j. The table values are wrong.
Nested loops unlock a wide range of algorithmic patterns. Each pattern pairs an outer traversal with an inner operation:
01
Grid operations
Process every cell in a 2D matrix using row and column loops
02
Combinations
Generate all pairs from two or more sets of values
03
Pattern printing
Build triangle, pyramid, and diamond shapes row by row
04
Multi-dim search
Find a target value by scanning rows then columns
05
Pair comparisons
Check every pair of items for duplicates or relationships
Loop else Clauses
Daily Life
Interviews
Detect when a search finds nothing
Python has a unique feature: you can attach an else clause to loops. The else block executes when the loop completes normally (without hitting a break). If break terminates the loop, the else block is skipped.
The for-else Pattern
The else block runs only if the loop completed without breaking:
1
# Search with for-else
2
numbers=[1,3,5,7,9]
3
4
# Looking for an even number
5
fornuminnumbers:
6
ifnum%2==0:
7
print("Found even:",num)
8
break
9
else:
10
print("No even numbers found")
11
12
print()
13
14
numbers2=[1,3,4,7,9]
15
16
fornuminnumbers2:
17
ifnum%2==0:
18
print("Found even:",num)
19
break
20
else:
21
print("No even numbers found")
>>>Output
No even numbers found
Found even: 4
Think of for-else as "for...else if no break". In the first loop, no even number exists, so the loop completes normally and else runs. In the second loop, break executes, so else is skipped.
The while-else Pattern
The else clause works identically with while loops:
1
# Try to find a value
2
target=42
3
attempts=0
4
max_attempts=3
5
current=10
6
7
result=None
8
whileattempts<max_attempts:
9
attempts+=1
10
current+=10
11
print("Attempt",attempts,"- current value:",current)
12
ifcurrent==target:
13
print("Found target!")
14
break
15
else:
16
print("Target not found after",max_attempts,"attempts")
17
18
print()
19
20
# Version where we find it
21
target=30
22
attempts=0
23
current=10
24
25
whileattempts<max_attempts:
26
attempts+=1
27
current+=10
28
print("Attempt",attempts,"- current value:",current)
29
ifcurrent==target:
30
print("Found target!")
31
break
32
else:
33
print("Target not found")
>>>Output
Attempt 1 - current value: 20
Attempt 2 - current value: 30
Attempt 3 - current value: 40
Target not found after 3 attempts
Attempt 1 - current value: 20
Attempt 2 - current value: 30
Found target!
Practical Use Cases
Loop-else is ideal for search patterns where you need to know if the search succeeded:
searchvalidateprimeretry
search
Find an item
else runs if not found
validate
Check all items
else confirms all passed
prime
Divisor search
else means no divisors
retry
Attempt loops
else means all retries failed
The prime number check is a classic example. The loop searches for a divisor. If it finds one, it breaks. If the loop finishes without breaking, the else clause confirms the number is prime:
1
# Check if number is prime
2
defis_prime(n):
3
ifn<2:
4
returnFalse
5
foriinrange(2,int(n**0.5)+1):
6
ifn%i==0:
7
returnFalse
8
returnTrue
9
10
# Using loop-else for same logic
11
n=17
12
foriinrange(2,int(n**0.5)+1):
13
ifn%i==0:
14
print(n,"is not prime")
15
break
16
else:
17
print(n,"is prime")
>>>Output
17 is prime
Mastering loop patterns helps you write more efficient and expressive code. Put these techniques to the test with hands-on challenges in the Python Builder.
❯❯❯PUTTING IT ALL TOGETHER
> You are a data engineer at Salesforce processing multi-dimensional user activity logs, pairing each user with their corresponding event sequence to calculate weekly engagement scores across product lines.
enumerate() pairs each user record with its position index so you can track progress and log which record triggered an anomaly.
zip() links the user list with the parallel event-sequence list so each engagement calculation draws from both in a single loop.
Nested loops iterate over each user's individual events within the outer pass over all users to accumulate weekly scores.
The loop else clause signals cleanly when a full user scan completes without finding an expected anchor event to score against.
KEY TAKEAWAYS
enumerate(sequence) yields (index, value) pairs; use start=1 to begin at 1
zip(seq1, seq2, ...) pairs items from multiple sequences; stops at shortest
Dictionary iteration: .keys(), .values(), .items() for key-value pairs
Basic enumerate Usage Starting at Any Index enumerate() Patterns enumerate() for Strings Enumerate works with any iterable, including strings. This is useful for finding character positions or processing text with position awareness: enumerate() for Progress When processing large datasets or files, enumerate helps display progress to users:
Basic zip Usage On the first iteration, you get ("Alice", 25). On the second, ("Bob", 30). The items are paired by their position in each list. zip() with Unequal Lengths When sequences have different lengths, zip stops at the shortest one: Knowing when to reach for enumerate versus zip versus plain iteration saves you from writing unnecessary boilerplate. Here is a concise guide. zip() with Multiple Lists You can zip together any number of sequences: Dicts from zip() A common pattern is using z
Dictionaries provide several methods for iteration: you can loop over just keys, just values, or key-value pairs together. Each approach is useful in different situations. Iterating Over Keys By default, iterating over a dictionary yields its keys: Iterating Over Values Iterating Key-Value Pairs Filtering Dict Entries Common patterns when working with dictionary iteration: Iterating Nested Dicts Real-world data often involves dictionaries containing other dictionaries. You can use nested loops t
A nested loop is a loop inside another loop. The inner loop runs completely for each iteration of the outer loop. This pattern is essential for working with multi-dimensional data structures and generating combinations. Basic Nested Loop The inner loop completes all its iterations before the outer loop moves to the next item: The outer loop runs 3 times. For each outer iteration, the inner loop runs 2 times. Total iterations: 3 x 2 = 6. Working with 2D Data Nested loops are natural for processin
The for-else Pattern The else block runs only if the loop completed without breaking: The while-else Pattern The else clause works identically with while loops: Practical Use Cases Loop-else is ideal for search patterns where you need to know if the search succeeded: The prime number check is a classic example. The loop searches for a divisor. If it finds one, it breaks. If the loop finishes without breaking, the else clause confirms the number is prime: Mastering loop patterns helps you write m