
Python
For loops are a cornerstone of Python programming. At an advanced level, mastering comprehensions, generator expressions, and powerful iteration tools transforms how you write efficient, readable code.
List comprehensions provide a concise and efficient way to create lists by filtering and transforming elements.
# Traditional approach
squares = []
for x in range(10):
squares.append(x**2)
# List comprehension (30-50% faster)
squares = [x**2 for x in range(10)]
# Performance comparison
import timeit
def traditional():
result = []
for x in range(1000):
result.append(x**2)
return result
def comprehension():
return [x**2 for x in range(1000)]
# List comprehension is typically 2-3x faster
print(timeit.timeit(traditional, number=10000)) # ~0.5s
print(timeit.timeit(comprehension, number=10000)) # ~0.15s# Multiple conditions
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
filtered = [x for x in numbers if x % 2 == 0 if x > 5]
# Result: [6, 8, 10]
# Nested iterations
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
flattened = [item for row in matrix for item in row]
# Result: [1, 2, 3, 4, 5, 6, 7, 8, 9]
# Conditional transformation
values = [1, 2, 3, 4, 5]
result = ['even' if x % 2 == 0 else 'odd' for x in values]
# Result: ['odd', 'even', 'odd', 'even', 'odd']
# String processing
words = ['hello', 'world', 'python']
capitalized = [word.upper() for word in words if len(word) > 4]
# Result: ['HELLO', 'WORLD', 'PYTHON']
# Type conversion and filtering
mixed = [1, '2', 3, '4', 5]
integers = [int(x) for x in mixed if isinstance(x, str)]
# Result: [2, 4]# Creating a 3x3 matrix
matrix = [[i*j for j in range(1, 4)] for i in range(1, 4)]
# Result: [[1, 2, 3], [2, 4, 6], [3, 6, 9]]
# Flattening with conditions
nested = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
evens = [x for row in nested for x in row if x % 2 == 0]
# Result: [2, 4, 6, 8]
# Cartesian product
colors = ['red', 'green', 'blue']
sizes = ['S', 'M', 'L']
combinations = [(color, size) for color in colors for size in sizes]
# Result: [('red', 'S'), ('red', 'M'), ... ('blue', 'L')]
# Dictionary from list
keys = ['a', 'b', 'c']
values = [1, 2, 3]
dictionary = {k: v for k, v in zip(keys, values)}
# Result: {'a': 1, 'b': 2, 'c': 3}# Dictionary comprehension
words = ['python', 'java', 'javascript', 'c']
word_lengths = {word: len(word) for word in words}
# Result: {'python': 6, 'java': 4, 'javascript': 10, 'c': 1}
# Dictionary with filtering
numbers = range(10)
even_squares = {x: x**2 for x in numbers if x % 2 == 0}
# Result: {0: 0, 2: 4, 4: 16, 6: 36, 8: 64}
# Swapping keys and values
prices = {'apple': 1.5, 'banana': 0.5, 'orange': 2.0}
prices_by_value = {v: k for k, v in prices.items()}
# Result: {1.5: 'apple', 0.5: 'banana', 2.0: 'orange'}
# Set comprehension (removes duplicates)
numbers = [1, 2, 2, 3, 3, 3, 4]
unique_squared = {x**2 for x in numbers}
# Result: {1, 4, 9, 16}
# Grouping by property
people = [
{'name': 'Alice', 'age': 25},
{'name': 'Bob', 'age': 30},
{'name': 'Charlie', 'age': 25}
]
by_age = {person['age']: [p['name'] for p in people if p['age'] == person['age']]
for person in people}Generator expressions are like list comprehensions but return an iterator instead of a list, using significantly less memory.
# List comprehension - loads entire list into memory
squares_list = [x**2 for x in range(10)]
# Generator expression - creates values on-demand
squares_gen = (x**2 for x in range(10))
# Memory comparison
import sys
list_comp = [x**2 for x in range(1000)]
gen_exp = (x**2 for x in range(1000))
print(sys.getsizeof(list_comp)) # ~9016 bytes
print(sys.getsizeof(gen_exp)) # ~128 bytes (80x smaller!)
# Generators are consumed (exhausted after iteration)
gen = (x for x in range(3))
print(list(gen)) # [0, 1, 2]
print(list(gen)) # [] - generator exhausted!import timeit
# Task: Sum squares up to 1,000,000
def list_approach():
squares = [x**2 for x in range(1000000)]
return sum(squares)
def generator_approach():
return sum(x**2 for x in range(1000000))
# Generators are faster and more memory-efficient
print(timeit.timeit(list_approach, number=5)) # ~0.8s
print(timeit.timeit(generator_approach, number=5)) # ~0.6s
# Memory profiling
import tracemalloc
tracemalloc.start()
list_result = [x**2 for x in range(100000)]
list_mem = tracemalloc.get_traced_memory()[0]
tracemalloc.reset_peak()
gen_result = (x**2 for x in range(100000))
gen_mem = sum(1 for _ in gen_result)
gen_memory_used = tracemalloc.get_traced_memory()[0]
print(f"List memory: {list_mem / 1024 / 1024:.2f} MB")
print(f"Generator memory: {gen_memory_used / 1024 / 1024:.2f} MB")# Filtering large datasets without loading into memory
def read_large_file(filepath):
with open(filepath, 'r') as file:
for line in file:
yield line.strip()
# Using generator for processing
def process_file(filepath):
for line in read_large_file(filepath):
if 'error' in line.lower():
yield line
# Chaining generators
def filter_and_transform(filepath):
for line in read_large_file(filepath):
if len(line) > 10:
yield line.upper()
# Lazy evaluation with conditions
numbers = range(1000000)
large_primes = (n for n in numbers if is_prime(n) and n > 100000)
def is_prime(n):
if n < 2:
return False
for i in range(2, int(n**0.5) + 1):
if n % i == 0:
return False
return True
# Generator with multiple filters
data = range(100)
result = (
x for x in data
if x % 2 == 0
if x % 3 == 0
if x > 10
)# Creating custom generators with yield
def countdown(n):
while n > 0:
yield n
n -= 1
# Using the generator
for num in countdown(5):
print(num) # Output: 5, 4, 3, 2, 1
# Fibonacci sequence generator
def fibonacci(max_n):
a, b = 0, 1
while a < max_n:
yield a
a, b = b, a + b
# Efficient memory usage for large sequences
for fib in fibonacci(1000000):
if fib > 50000:
break
process(fib)
# Generator with two-way communication
def echo():
while True:
x = yield
print(f"Received: {x}")
gen = echo()
next(gen) # Prime the generator
gen.send("hello") # Output: Received: hello
gen.send("world") # Output: Received: worldThe `itertools` module provides powerful tools for creating efficient iterators.
from itertools import (
islice, chain, repeat, cycle, combinations,
permutations, product, groupby, accumulate
)
# islice: Get first n items or slice an iterator
numbers = range(100)
first_10 = list(islice(numbers, 10))
# chain: Combine multiple iterables
combined = list(chain([1, 2], [3, 4], [5, 6]))
# Result: [1, 2, 3, 4, 5, 6]
# repeat: Repeat a value
repeated = list(repeat('x', 5))
# Result: ['x', 'x', 'x', 'x', 'x']
# cycle: Cycle through iterable indefinitely
colors = cycle(['red', 'green', 'blue'])
for _ in range(9):
print(next(colors)) # Alternates through colors
# combinations: All combinations of length r
from itertools import combinations
items = [1, 2, 3, 4]
pairs = list(combinations(items, 2))
# Result: [(1, 2), (1, 3), (1, 4), (2, 3), (2, 4), (3, 4)]
# permutations: All ordered arrangements
perms = list(permutations([1, 2, 3], 2))
# Result: [(1, 2), (1, 3), (2, 1), (2, 3), (3, 1), (3, 2)]
# product: Cartesian product
colors = ['red', 'green']
sizes = ['S', 'M', 'L']
combinations = list(product(colors, sizes))
# Result: [('red', 'S'), ('red', 'M'), ('red', 'L'), ('green', 'S'), ...]from itertools import groupby, accumulate, compress, dropwhile, takewhile
# groupby: Group consecutive elements
from operator import itemgetter
data = [(1, 'a'), (1, 'b'), (2, 'a'), (2, 'c'), (3, 'a')]
for key, group in groupby(data, key=itemgetter(0)):
print(key, list(group))
# accumulate: Running totals
from itertools import accumulate
values = [1, 2, 3, 4, 5]
running_sum = list(accumulate(values))
# Result: [1, 3, 6, 10, 15]
running_product = list(accumulate(values, lambda x, y: x * y))
# Result: [1, 2, 6, 24, 120]
# compress: Filter elements based on selectors
from itertools import compress
data = ['a', 'b', 'c', 'd', 'e']
selectors = [1, 0, 1, 0, 1]
filtered = list(compress(data, selectors))
# Result: ['a', 'c', 'e']
# dropwhile and takewhile: Conditional slicing
from itertools import dropwhile, takewhile
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9]
greater_than_5 = list(dropwhile(lambda x: x <= 5, numbers))
# Result: [6, 7, 8, 9]
less_than_7 = list(takewhile(lambda x: x < 7, numbers))
# Result: [1, 2, 3, 4, 5, 6]# Paging through large datasets
from itertools import islice
def paginate(iterable, page_size):
iterator = iter(iterable)
while True:
page = list(islice(iterator, page_size))
if not page:
break
yield page
# Usage
for page_num, page in enumerate(paginate(range(100), 10)):
print(f"Page {page_num}: {page}")
# Round-robin scheduling
from itertools import cycle, islice
def round_robin(*iterables):
iterators = [iter(it) for it in iterables]
active = list(range(len(iterators)))
while active:
for i in active[:]:
try:
yield next(iterators[i])
except StopIteration:
active.remove(i)
# Combining multiple sources
lists = [[1, 2], ['a', 'b'], [10, 20]]
result = list(round_robin(*lists))
# Yields: 1, 'a', 10, 2, 'b', 20
# Sliding window (k-gram)
def sliding_window(iterable, window_size):
from itertools import islice
iterator = iter(iterable)
window = tuple(islice(iterator, window_size))
if len(window) == window_size:
yield window
for item in iterator:
window = window[1:] + (item,)
yield window
# Usage
text = "hello"
for bigram in sliding_window(text, 2):
print(bigram) # ('h', 'e'), ('e', 'l'), ('l', 'l'), ('l', 'o')# Task: Create first 10 even squares
# 1. Traditional loop (clear but verbose)
result = []
for x in range(100):
if x % 2 == 0:
result.append(x**2)
if len(result) == 10:
break
# 2. List comprehension with slicing (concise)
result = [x**2 for x in range(100) if x % 2 == 0][:10]
# 3. Generator with islice (memory efficient)
from itertools import islice
result = list(islice((x**2 for x in range(100) if x % 2 == 0), 10))
# Performance: generators are best for large datasets
# Readability: list comprehensions for small datasets
# Balance: generators with itertools for production code# Unfused loops (multiple passes)
def process_data(items):
# First pass
filtered = [x for x in items if x > 10]
# Second pass
transformed = [x * 2 for x in filtered]
# Third pass
validated = [x for x in transformed if x < 100]
return validated
# Fused loop (single pass)
def process_data_optimized(items):
return [
x * 2
for x in items
if x > 10
if x * 2 < 100
]
# With early termination
def first_n_valid(items, n=100):
from itertools import islice
gen = (x * 2 for x in items if x > 10)
return list(islice(gen, n))| Technique | Speed | Memory | Readability | Use Case |
|---|---|---|---|---|
| List Comprehension | Very Fast | High | Excellent | Small-medium lists |
| Generator Expression | Very Fast | Very Low | Excellent | Large datasets, streaming |
| Traditional Loop | Slow | Varies | Good | Complex logic |
| itertools | Very Fast | Very Low | Good | Specialized iteration |
| Nested Comprehension | Fast | Medium-High | Fair | Multi-dimensional data |
Explore these advanced topics:
Master for loops and unlock Python's full potential!
Resources
Ojasa Mirai
Master AI-powered development skills through structured learning, real projects, and verified credentials. Whether you're upskilling your team or launching your career, we deliver the skills companies actually need.
Learn Deep • Build Real • Verify Skills • Launch Forward