Master Python's most elegant functional programming features
List comprehensions provide a concise way to create lists. They're more readable and often faster than traditional for loops for creating lists.
The basic syntax follows the pattern: [expression for item in iterable]
# Traditional approach
squares = []
for i in range(10):
squares.append(i**2)
# List comprehension approach
squares = [i**2 for i in range(10)]
print(squares) # [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
# More examples
words = ['hello', 'world', 'python', 'list']
lengths = [len(word) for word in words]
print(lengths) # [5, 5, 6, 4]
# String operations
names = ['alice', 'bob', 'charlie']
capitalized = [name.capitalize() for name in names]
print(capitalized) # ['Alice', 'Bob', 'Charlie']
# Mathematical operations
numbers = [1, 2, 3, 4, 5]
doubled = [x * 2 for x in numbers]
cubed = [x**3 for x in numbers]
print(doubled) # [2, 4, 6, 8, 10]
print(cubed) # [1, 8, 27, 64, 125]
Add filtering and conditional logic to your comprehensions.
# Filter even numbers
numbers = range(10)
evens = [x for x in numbers if x % 2 == 0]
print(evens) # [0, 2, 4, 6, 8]
# Filter strings by length
words = ['a', 'hello', 'hi', 'python', 'world']
long_words = [word for word in words if len(word) > 3]
print(long_words) # ['hello', 'python', 'world']
# Multiple conditions
numbers = range(20)
result = [x for x in numbers if x % 2 == 0 if x % 3 == 0]
print(result) # [0, 6, 12, 18] (divisible by both 2 and 3)
# Alternative: using 'and'
result = [x for x in numbers if x % 2 == 0 and x % 3 == 0]
# Conditional expression in the output
numbers = range(-5, 6)
abs_values = [x if x >= 0 else -x for x in numbers]
print(abs_values) # [5, 4, 3, 2, 1, 0, 1, 2, 3, 4, 5]
# Categorize numbers
categories = ['positive' if x > 0 else 'negative' if x < 0 else 'zero'
for x in numbers]
print(categories) # ['negative', 'negative', ..., 'zero', 'positive', ...]
# Replace values
data = [1, 2, 0, 4, 0, 6]
cleaned = [x if x != 0 else 'missing' for x in data]
print(cleaned) # [1, 2, 'missing', 4, 'missing', 6]
[expression for item in iterable if condition]
filters items, while [expression if condition else alternative for item in iterable]
applies conditional logic to the expression.
Handle nested data structures and create complex transformations.
# Flatten a matrix
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
flattened = [num for row in matrix for num in row]
print(flattened) # [1, 2, 3, 4, 5, 6, 7, 8, 9]
# Create multiplication table
multiplication_table = [[i * j for j in range(1, 4)] for i in range(1, 4)]
print(multiplication_table) # [[1, 2, 3], [2, 4, 6], [3, 6, 9]]
# Transpose matrix
transposed = [[row[i] for row in matrix] for i in range(len(matrix[0]))]
print(transposed) # [[1, 4, 7], [2, 5, 8], [3, 6, 9]]
# Extract specific data from nested dictionaries
students = [
{'name': 'Alice', 'grades': [85, 90, 78]},
{'name': 'Bob', 'grades': [92, 88, 95]},
{'name': 'Charlie', 'grades': [78, 85, 82]}
]
# Get all grades
all_grades = [grade for student in students for grade in student['grades']]
print(all_grades) # [85, 90, 78, 92, 88, 95, 78, 85, 82]
# Get high grades only
high_grades = [grade for student in students for grade in student['grades']
if grade >= 85]
print(high_grades) # [85, 90, 92, 88, 95, 85]
# Nested comprehension with conditions
coordinates = [(x, y) for x in range(3) for y in range(3) if x != y]
print(coordinates) # [(0, 1), (0, 2), (1, 0), (1, 2), (2, 0), (2, 1)]
Understanding when to use generator expressions for memory efficiency.
# List comprehension - creates entire list in memory
list_comp = [x**2 for x in range(1000000)]
print(type(list_comp)) #
# Generator expression - creates generator object
gen_exp = (x**2 for x in range(1000000))
print(type(gen_exp)) #
# Memory usage comparison
import sys
list_size = sys.getsizeof([x for x in range(1000)])
gen_size = sys.getsizeof((x for x in range(1000)))
print(f"List: {list_size} bytes, Generator: {gen_size} bytes")
# Using generator expressions
def process_large_file():
# Efficient for large datasets
return (line.strip().upper() for line in open('file.txt'))
# Convert generator to list when needed
squares_gen = (x**2 for x in range(10))
squares_list = list(squares_gen)
print(squares_list) # [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
# Set comprehensions
numbers = [1, 2, 2, 3, 3, 4, 5]
unique_squares = {x**2 for x in numbers}
print(unique_squares) # {1, 4, 9, 16, 25}
# Dictionary comprehensions
words = ['hello', 'world', 'python']
word_lengths = {word: len(word) for word in words}
print(word_lengths) # {'hello': 5, 'world': 5, 'python': 6}
# Dictionary comprehension with conditions
numbers = range(10)
even_squares = {x: x**2 for x in numbers if x % 2 == 0}
print(even_squares) # {0: 0, 2: 4, 4: 16, 6: 36, 8: 64}
Lambda functions are small anonymous functions that can have any number of arguments but can only have one expression.
Basic syntax: lambda arguments: expression
# Basic lambda function
square = lambda x: x**2
print(square(5)) # 25
# Multiple arguments
add = lambda x, y: x + y
print(add(3, 7)) # 10
# Lambda with default arguments
greet = lambda name, greeting="Hello": f"{greeting}, {name}!"
print(greet("Alice")) # Hello, Alice!
print(greet("Bob", "Hi")) # Hi, Bob!
# Conditional lambda
max_of_two = lambda x, y: x if x > y else y
print(max_of_two(10, 5)) # 10
# Lambda returning lambda (higher-order function)
multiply_by = lambda n: lambda x: x * n
double = multiply_by(2)
triple = multiply_by(3)
print(double(5)) # 10
print(triple(5)) # 15
# Sorting with custom key
students = [('Alice', 85), ('Bob', 90), ('Charlie', 78)]
# Sort by grade (second element)
sorted_by_grade = sorted(students, key=lambda x: x[1])
print(sorted_by_grade) # [('Charlie', 78), ('Alice', 85), ('Bob', 90)]
# Sort by name length
words = ['python', 'java', 'c', 'javascript']
sorted_by_length = sorted(words, key=lambda x: len(x))
print(sorted_by_length) # ['c', 'java', 'python', 'javascript']
# Complex sorting
data = [{'name': 'Alice', 'age': 25, 'score': 85},
{'name': 'Bob', 'age': 23, 'score': 90},
{'name': 'Charlie', 'age': 25, 'score': 78}]
# Sort by age, then by score
sorted_data = sorted(data, key=lambda x: (x['age'], x['score']))
print(sorted_data)
# Event handling (conceptual example)
button_actions = {
'save': lambda: print("File saved!"),
'load': lambda: print("File loaded!"),
'exit': lambda: print("Goodbye!")
}
button_actions['save']() # File saved!
# These don't work with lambda:
# lambda x: print(x) # print is a statement
# lambda x: return x # return is a statement
# lambda x: pass # pass is a statement
# These work:
# Use expression equivalents
output = lambda x: x # Instead of print, return the value
condition = lambda x: x if x > 0 else 0 # Instead of if statement
Functional programming tools that work beautifully with lambda functions.
Applies a function to every item in an iterable.
# Basic map usage
numbers = [1, 2, 3, 4, 5]
squares = list(map(lambda x: x**2, numbers))
print(squares) # [1, 4, 9, 16, 25]
# Multiple iterables
list1 = [1, 2, 3, 4]
list2 = [10, 20, 30, 40]
products = list(map(lambda x, y: x * y, list1, list2))
print(products) # [10, 40, 90, 160]
# String operations
words = ['hello', 'world', 'python']
capitalized = list(map(lambda x: x.upper(), words))
print(capitalized) # ['HELLO', 'WORLD', 'PYTHON']
# Complex transformations
data = ['1,2,3', '4,5,6', '7,8,9']
parsed = list(map(lambda x: [int(i) for i in x.split(',')], data))
print(parsed) # [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
# Map vs List comprehension
# These are equivalent:
result1 = list(map(lambda x: x**2, range(5)))
result2 = [x**2 for x in range(5)]
print(result1 == result2) # True
Filters elements from an iterable based on a condition.
# Basic filter usage
numbers = range(10)
evens = list(filter(lambda x: x % 2 == 0, numbers))
print(evens) # [0, 2, 4, 6, 8]
# Filter strings
words = ['apple', 'banana', 'cherry', 'date', 'elderberry']
long_words = list(filter(lambda x: len(x) > 5, words))
print(long_words) # ['banana', 'cherry', 'elderberry']
# Filter with complex conditions
students = [
{'name': 'Alice', 'grade': 85, 'age': 20},
{'name': 'Bob', 'grade': 92, 'age': 19},
{'name': 'Charlie', 'grade': 78, 'age': 21},
{'name': 'Diana', 'grade': 88, 'age': 20}
]
high_achievers = list(filter(lambda s: s['grade'] > 80 and s['age'] < 21, students))
print(high_achievers) # [{'name': 'Alice', ...}, {'name': 'Bob', ...}]
# Remove None values
mixed_data = [1, None, 2, None, 3, 0, 4]
cleaned = list(filter(lambda x: x is not None, mixed_data))
print(cleaned) # [1, 2, 3, 0, 4]
# Filter vs List comprehension
# These are equivalent:
result1 = list(filter(lambda x: x > 0, [-2, -1, 0, 1, 2]))
result2 = [x for x in [-2, -1, 0, 1, 2] if x > 0]
print(result1 == result2) # True
Applies a rolling computation to sequential pairs of values in a list.
from functools import reduce
# Basic reduce usage - sum
numbers = [1, 2, 3, 4, 5]
total = reduce(lambda x, y: x + y, numbers)
print(total) # 15
# Product of all numbers
product = reduce(lambda x, y: x * y, numbers)
print(product) # 120
# Find maximum
numbers = [3, 7, 2, 9, 1, 5]
maximum = reduce(lambda x, y: x if x > y else y, numbers)
print(maximum) # 9
# String concatenation
words = ['Hello', ' ', 'World', '!']
sentence = reduce(lambda x, y: x + y, words)
print(sentence) # Hello World!
# With initial value
numbers = [1, 2, 3, 4, 5]
total_with_initial = reduce(lambda x, y: x + y, numbers, 10)
print(total_with_initial) # 25 (10 + 1 + 2 + 3 + 4 + 5)
# Complex reduce operations
transactions = [100, -50, 200, -75, 150]
final_balance = reduce(lambda balance, transaction: balance + transaction,
transactions, 0)
print(final_balance) # 325
# Process data through multiple stages
data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
# Filter evens, square them, then sum
result = reduce(lambda x, y: x + y,
map(lambda x: x**2,
filter(lambda x: x % 2 == 0, data)))
print(result) # 220 (2² + 4² + 6² + 8² + 10² = 4 + 16 + 36 + 64 + 100)
# More readable version using variables
evens = filter(lambda x: x % 2 == 0, data)
squares = map(lambda x: x**2, evens)
total = reduce(lambda x, y: x + y, squares)
print(total) # 220
Powerful patterns that combine the elegance of both features.
# Using lambda in list comprehensions
numbers = [1, 2, 3, 4, 5]
operations = [lambda x: x**2, lambda x: x**3, lambda x: x*2]
# Apply each operation to each number
results = [[op(num) for op in operations] for num in numbers]
print(results) # [[1, 1, 2], [4, 8, 4], [9, 27, 6], [16, 64, 8], [25, 125, 10]]
# Create function mapping
func_map = {f'op{i}': op for i, op in enumerate(operations)}
applied = [func_map['op0'](x) for x in numbers] # Apply first operation
print(applied) # [1, 4, 9, 16, 25]
# Dynamic function creation
multipliers = [lambda x, n=i: x * n for i in range(1, 6)]
results = [mult(10) for mult in multipliers]
print(results) # [10, 20, 30, 40, 50]
# List comprehension equivalent to map + filter
data = range(10)
# Using map and filter
result1 = list(map(lambda x: x**2, filter(lambda x: x % 2 == 0, data)))
# Using list comprehension
result2 = [x**2 for x in data if x % 2 == 0]
print(result1 == result2) # True
# Complex data processing
students = [
{'name': 'Alice', 'scores': [85, 90, 78]},
{'name': 'Bob', 'scores': [92, 88, 95]},
{'name': 'Charlie', 'scores': [78, 85, 82]}
]
# Calculate average scores using comprehension + lambda
averages = [(student['name'],
sum(student['scores']) / len(student['scores']))
for student in students]
print(averages) # [('Alice', 84.33), ('Bob', 91.67), ('Charlie', 81.67)]
# Using map with comprehension results
words = ['hello', 'world', 'python']
word_data = [{'word': word, 'length': len(word), 'upper': word.upper()}
for word in words]
lengths = list(map(lambda x: x['length'], word_data))
print(lengths) # [5, 5, 6]
# Matrix operations with comprehensions and lambdas
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
# Apply function to each element
apply_func = lambda x: x**2
squared_matrix = [[apply_func(cell) for cell in row] for row in matrix]
print(squared_matrix) # [[1, 4, 9], [16, 25, 36], [49, 64, 81]]
# Conditional operations
transform = lambda x: x * 2 if x % 2 == 0 else x * 3
transformed = [[transform(cell) for cell in row] for row in matrix]
print(transformed) # [[3, 4, 9], [8, 15, 12], [21, 16, 27]]
# Functional pipeline
def process_data(data, *functions):
"""Apply a series of functions to data"""
result = data
for func in functions:
result = func(result)
return result
# Example usage
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
result = process_data(
numbers,
lambda x: filter(lambda n: n % 2 == 0, x), # Filter evens
lambda x: map(lambda n: n**2, x), # Square them
lambda x: [n for n in x if n > 10], # Filter > 10
list # Convert to list
)
print(result) # [16, 36, 64, 100]
Understanding the performance characteristics of comprehensions and lambdas.
import time
# Performance test function
def time_function(func, *args, iterations=1000000):
start = time.time()
for _ in range(iterations):
func(*args)
end = time.time()
return end - start
# Test data
numbers = list(range(100))
# List comprehension vs for loop
def for_loop_squares(nums):
result = []
for num in nums:
result.append(num**2)
return result
def list_comp_squares(nums):
return [num**2 for num in nums]
# List comprehension vs map
def map_squares(nums):
return list(map(lambda x: x**2, nums))
# Performance results (approximate)
print("For loop:", time_function(for_loop_squares, numbers))
print("List comp:", time_function(list_comp_squares, numbers))
print("Map + lambda:", time_function(map_squares, numbers))
import sys
# Memory comparison
def memory_usage_example():
n = 10000
# List comprehension
list_comp = [x**2 for x in range(n)]
list_size = sys.getsizeof(list_comp)
# Generator expression
gen_exp = (x**2 for x in range(n))
gen_size = sys.getsizeof(gen_exp)
print(f"List comprehension: {list_size} bytes")
print(f"Generator expression: {gen_size} bytes")
print(f"Ratio: {list_size / gen_size:.1f}x larger")
memory_usage_example()
The most common pitfall when using lambdas in loops.
# WRONG: Late binding trap
functions = []
for i in range(5):
functions.append(lambda: i) # All lambdas capture the same 'i'
# All functions return 4 (the final value of i)
for func in functions:
print(func()) # 4, 4, 4, 4, 4
# CORRECT: Early binding with default parameter
functions = []
for i in range(5):
functions.append(lambda x=i: x) # Capture current value of i
for func in functions:
print(func()) # 0, 1, 2, 3, 4
# ALTERNATIVE: Using list comprehension (recommended)
functions = [lambda x=i: x for i in range(5)]
for func in functions:
print(func()) # 0, 1, 2, 3, 4
# AVOID: Complex nested comprehensions
result = [[func(x) for func in [lambda n: n**2, lambda n: n**3]]
for x in range(5) if x % 2 == 0]
# BETTER: Break it down
operations = [lambda n: n**2, lambda n: n**3]
even_numbers = [x for x in range(5) if x % 2 == 0]
result = [[func(x) for func in operations] for x in even_numbers]
# EVEN BETTER: Use named functions
def square(n): return n**2
def cube(n): return n**3
operations = [square, cube]
result = [[func(x) for func in operations] for x in even_numbers]
# AVOID: Side effects in comprehensions
# BAD
[print(x) for x in range(5)] # Don't use comprehensions for side effects
# GOOD
for x in range(5):
print(x)
# AVOID: Complex logic
# BAD
result = [x if x > 0 else -x if x < 0 else 0 for x in numbers]
# GOOD
def absolute_value(x):
if x > 0:
return x
elif x < 0:
return -x
else:
return 0
result = [absolute_value(x) for x in numbers]
Flatten a list of lists using list comprehension.
# Input
nested = [[1, 2], [3, 4, 5], [6]]
# Output: [1, 2, 3, 4, 5, 6]
# Solution
flattened = [item for sublist in nested for item in sublist]
Find all prime numbers in a range using filter and lambda.
# Input: numbers 2 to 20
# Output: [2, 3, 5, 7, 11, 13, 17, 19]
# Solution
is_prime = lambda n: n > 1 and all(n % i != 0 for i in range(2, int(n**0.5) + 1))
primes = list(filter(is_prime, range(2, 21)))
Create a dictionary from two lists using comprehension.
# Input
keys = ['a', 'b', 'c']
values = [1, 2, 3]
# Output: {'a': 1, 'b': 2, 'c': 3}
# Solution
result = {k: v for k, v in zip(keys, values)}
Count word frequencies using comprehension and reduce.
# Input
text = "hello world hello python world"
# Output: {'hello': 2, 'world': 2, 'python': 1}
# Solution
from functools import reduce
words = text.split()
freq = reduce(lambda acc, word: {**acc, word: acc.get(word, 0) + 1}, words, {})
Transpose a matrix using list comprehension.
# Input
matrix = [[1, 2, 3], [4, 5, 6]]
# Output: [[1, 4], [2, 5], [3, 6]]
# Solution
transposed = [[row[i] for row in matrix] for i in range(len(matrix[0]))]
Sort students by grade descending, then by name ascending.
# Input
students = [('Alice', 85), ('Bob', 90), ('Charlie', 85)]
# Output: [('Bob', 90), ('Alice', 85), ('Charlie', 85)]
# Solution
sorted_students = sorted(students, key=lambda x: (-x[1], x[0]))
Generate Fibonacci sequence using generator expression and lambda.
# Create first 10 Fibonacci numbers
def fibonacci_gen():
a, b = 0, 1
while True:
yield a
a, b = b, a + b
fib_gen = fibonacci_gen()
first_10 = [next(fib_gen) for _ in range(10)]
print(first_10) # [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
Process nested JSON-like data structure.
# Extract all email addresses from nested user data
users = [
{'name': 'Alice', 'contacts': [{'type': 'email', 'value': 'alice@email.com'}]},
{'name': 'Bob', 'contacts': [{'type': 'phone', 'value': '123'}, {'type': 'email', 'value': 'bob@email.com'}]}
]
# Solution
emails = [contact['value'] for user in users
for contact in user['contacts']
if contact['type'] == 'email']
print(emails) # ['alice@email.com', 'bob@email.com']
# Basic: [expression for item in iterable]
squares = [x**2 for x in range(10)]
# With condition: [expression for item in iterable if condition]
evens = [x for x in range(10) if x % 2 == 0]
# With conditional expression: [expr1 if condition else expr2 for item in iterable]
signs = ['positive' if x > 0 else 'negative' for x in numbers]
# Nested: [expression for item1 in iterable1 for item2 in iterable2]
pairs = [(x, y) for x in range(3) for y in range(3)]
# Basic: lambda arguments: expression
add = lambda x, y: x + y
# With default args: lambda x, y=default: expression
greet = lambda name, msg="Hello": f"{msg}, {name}"
# Higher-order: lambda that returns lambda
multiply_by = lambda n: lambda x: x * n
# Map: apply function to all items
squared = list(map(lambda x: x**2, [1, 2, 3, 4]))
# Filter: select items that match condition
evens = list(filter(lambda x: x % 2 == 0, range(10)))
# Reduce: accumulate values (need to import)
from functools import reduce
total = reduce(lambda x, y: x + y, [1, 2, 3, 4, 5])
# Set comprehension
unique_squares = {x**2 for x in [1, 2, 2, 3, 3]}
# Dictionary comprehension
word_lengths = {word: len(word) for word in ['hello', 'world']}
# Generator expression (memory efficient)
gen = (x**2 for x in range(1000000))
# Flatten nested list
flat = [item for sublist in nested for item in sublist]
# Matrix transpose
transposed = [[row[i] for row in matrix] for i in range(len(matrix[0]))]
# Dictionary inversion
inverted = {v: k for k, v in original.items()}
# Conditional filtering
filtered = [x for x in data if condition(x)]
# Use generator for large data
gen = (process(x) for x in huge_list)
# Chain operations efficiently
result = [transform(x) for x in data if filter_condition(x)]
# Avoid side effects in comprehensions
# DON'T: [print(x) for x in data]
# DO: for x in data: print(x)