Technology  /  Python

🐍 Python 78 guides · updated 2026

From first variable to OOP, generators, and real projects β€” the language that runs everything from data pipelines to AI agents, taught the practical way.

Iterating Python Collections: Lists, Dicts, Sets, Tuples, and Generator Objects

Python’s for loop works on anything iterable β€” lists, tuples, dicts, sets, strings, generators, and custom objects that implement __iter__. The loop mechanics are the same, but each collection type has its own iteration behaviour worth understanding.

Lists and Tuples

Both are ordered sequences. Iteration visits elements left to right.

# Basic iteration
fruits = ["apple", "banana", "cherry"]
for fruit in fruits:
print(fruit)
# With index β€” use enumerate, not range(len())
for index, fruit in enumerate(fruits):
print(f"{index}: {fruit}")
# 0: apple, 1: banana, 2: cherry
# Custom start index
for index, fruit in enumerate(fruits, start=1):
print(f"{index}: {fruit}")
# 1: apple, 2: banana, 3: cherry
# Tuple iteration works identically
coords = (10, 20, 30)
for c in coords:
print(c)

enumerate is the idiomatic way to get index + value. range(len(lst)) works but is considered un-Pythonic.

Dictionaries

Iterating a dict directly yields keys. Use .values() or .items() for other views.

scores = {"Alice": 92, "Bob": 85, "Carol": 78}
# Iterate keys (default)
for name in scores:
print(name) # Alice, Bob, Carol
# Iterate values
for score in scores.values():
print(score) # 92, 85, 78
# Iterate key-value pairs β€” most common pattern
for name, score in scores.items():
print(f"{name}: {score}")
# Build a new dict while iterating
above_80 = {k: v for k, v in scores.items() if v > 80}
print(above_80) # {'Alice': 92, 'Bob': 85}
# Do not modify the dict while iterating its keys β€” use a copy
to_delete = [k for k, v in scores.items() if v < 90]
for key in to_delete:
del scores[key]

Sets

Sets are unordered β€” you cannot predict the iteration order, and it is not guaranteed to match insertion order.

unique_tags = {"python", "data", "tutorial", "python", "tips"}
print(unique_tags) # {'data', 'python', 'tips', 'tutorial'} β€” order varies
for tag in unique_tags:
print(tag) # some order, not necessarily insertion order
# If you need a consistent order, sort first
for tag in sorted(unique_tags):
print(tag) # alphabetical: data, python, tips, tutorial

Strings

Strings are iterable β€” each iteration step yields one character.

word = "Python"
for char in word:
print(char) # P, y, t, h, o, n
# Count vowels
vowels = sum(1 for c in word.lower() if c in "aeiou")
print(f"Vowels in '{word}': {vowels}")
# Reverse iteration
for char in reversed(word):
print(char) # n, o, h, t, y, P

zip β€” Iterating Multiple Collections Together

names = ["Alice", "Bob", "Carol"]
scores = [92, 85, 78]
grades = ["A", "B", "C"]
# zip stops at the shortest iterable
for name, score, grade in zip(names, scores, grades):
print(f"{name}: {score} ({grade})")
# zip_longest to keep going to the longest
from itertools import zip_longest
long = [1, 2, 3, 4, 5]
short = ["a", "b", "c"]
for num, letter in zip_longest(long, short, fillvalue="?"):
print(num, letter)
# 1 a, 2 b, 3 c, 4 ?, 5 ?

Generators β€” Lazy Iteration

Generators produce values one at a time rather than storing the whole collection in memory. Use them for large data streams.

# Generator expression β€” like comprehension, but lazy
gen = (x**2 for x in range(1_000_000)) # nothing computed yet
# Pull values one at a time
print(next(gen)) # 0
print(next(gen)) # 1
print(next(gen)) # 4
# Or iterate with for β€” only one value in memory at a time
for val in (x**2 for x in range(10)):
print(val, end=" ")
# 0 1 4 9 16 25 36 49 64 81
# Generator function
def fibonacci_gen():
a, b = 0, 1
while True:
yield a
a, b = b, a + b
from itertools import islice
print(list(islice(fibonacci_gen(), 10)))
# [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]

itertools β€” Advanced Iteration Patterns

from itertools import chain, product, groupby
# chain β€” iterate multiple iterables as one
combined = chain([1, 2], [3, 4], [5, 6])
print(list(combined)) # [1, 2, 3, 4, 5, 6]
# product β€” cartesian product
for size, colour in product(["S", "M", "L"], ["red", "blue"]):
print(f"{size}-{colour}")
# groupby β€” group consecutive equal elements (input must be sorted by key)
data = sorted(["apple", "avocado", "banana", "blueberry", "cherry"], key=lambda x: x[0])
for letter, group in groupby(data, key=lambda x: x[0]):
print(f"{letter}: {list(group)}")
# a: ['apple', 'avocado']
# b: ['banana', 'blueberry']
# c: ['cherry']

Custom Iterable

Any class with __iter__ and __next__ is iterable:

class CountDown:
def __init__(self, start):
self.current = start
def __iter__(self):
return self
def __next__(self):
if self.current <= 0:
raise StopIteration
self.current -= 1
return self.current + 1
for n in CountDown(5):
print(n, end=" ")
# 5 4 3 2 1

The for loop calls __iter__() to get the iterator, then repeatedly calls __next__() until StopIteration is raised.