Skip to content

Functional Programming Cheatsheet

Quick reference for functional programming in Python: lambdas, higher-order functions, map/filter/reduce, closures, and decorators.


Pure Functions

What Makes a Function Pure?

# Pure: Same input → Same output, no side effects
def add(a, b):
    return a + b

# Impure: Has side effects
def impure_add(a, b):
    print("Adding...")      # Side effect
    return a + b

# Impure: Depends on external state
counter = 0
def increment():
    global counter
    counter += 1           # Modifies external state
    return counter

Benefits of Pure Functions

  • Predictable: Same input always gives same output
  • Testable: Easy to unit test
  • Cacheable: Results can be memoized
  • Parallelizable: No shared state issues

Lambda Functions

Syntax & Basics

# Basic lambda
add = lambda x, y: x + y
print(add(2, 3))          # 5

# Without name (immediately invoked)
print((lambda x: x * 2)(5))  # 10

# Multiple parameters
mult = lambda x, y, z: x * y * z
print(mult(2, 3, 4))      # 24

Lambda Use Cases

# With sort (key function)
people = [("Alice", 25), ("Bob", 20), ("Charlie", 30)]
people.sort(key=lambda p: p[1])  # Sort by age
# [("Bob", 20), ("Alice", 25), ("Charlie", 30)]

# With max/min
people = [{"name": "Alice", "age": 25}, {"name": "Bob", "age": 20}]
oldest = max(people, key=lambda p: p["age"])

# With filter
numbers = [1, 2, 3, 4, 5]
evens = list(filter(lambda x: x % 2 == 0, numbers))  # [2, 4]

# With map
squares = list(map(lambda x: x ** 2, numbers))  # [1, 4, 9, 16, 25]

Lambda vs Regular Function

# Lambda (short, simple)
is_even = lambda x: x % 2 == 0

# Regular function (can do more)
def is_even(x):
    """Check if number is even."""
    return x % 2 == 0

# Use lambda for short, throwaway functions
# Use regular functions for anything reusable or complex

Higher-Order Functions

Functions as Arguments

def apply_operation(x, y, operation):
    """Apply operation to x and y."""
    return operation(x, y)

result = apply_operation(5, 3, lambda a, b: a + b)  # 8
result = apply_operation(5, 3, lambda a, b: a * b)  # 15

Functions Returning Functions

def make_multiplier(n):
    """Return a function that multiplies by n."""
    return lambda x: x * n

times_three = make_multiplier(3)
print(times_three(10))  # 30

times_five = make_multiplier(5)
print(times_five(10))   # 50

Built-in Higher-Order Functions

map()

# Apply function to all elements
numbers = [1, 2, 3, 4, 5]

# Map single function
squares = list(map(lambda x: x ** 2, numbers))
# [1, 4, 9, 16, 25]

# Map with two iterables
numbers1 = [1, 2, 3]
numbers2 = [4, 5, 6]
sums = list(map(lambda x, y: x + y, numbers1, numbers2))
# [5, 7, 9]

# List comprehension alternative
squares = [x ** 2 for x in numbers]

filter()

numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

# Filter even numbers
evens = list(filter(lambda x: x % 2 == 0, numbers))
# [2, 4, 6, 8, 10]

# Filter strings longer than 5
words = ["apple", "banana", "cherry", "date"]
long_words = list(filter(lambda w: len(w) > 5, words))
# ["banana", "cherry"]

# List comprehension alternative
evens = [x for x in numbers if x % 2 == 0]

reduce()

from functools import reduce

numbers = [1, 2, 3, 4, 5]

# Sum all numbers
total = reduce(lambda acc, x: acc + x, numbers)
# 15

# Product of all numbers
product = reduce(lambda acc, x: acc * x, numbers)
# 120

# Find maximum
maximum = reduce(lambda acc, x: acc if acc > x else x, numbers)
# 5

# With initial value
total = reduce(lambda acc, x: acc + x, numbers, 10)
# 25 (starts with 10)

sorted() with key

people = [("Alice", 25), ("Bob", 20), ("Charlie", 30)]

# Sort by age
sorted_people = sorted(people, key=lambda p: p[1])
# [("Bob", 20), ("Alice", 25), ("Charlie", 30)]

# Sort by name length
sorted_by_len = sorted(people, key=lambda p: len(p[0]))

Closures

Basic Closure

def outer_function(x):
    """Outer function creates closure."""

    def inner_function(y):
        """Inner function captures x from outer scope."""
        return x + y

    return inner_function  # Return the closure

# Create closure
add_five = outer_function(5)

# Use closure
print(add_five(3))  # 8
print(add_five(10)) # 15

Closure for State

def make_counter():
    """Create a counter function that remembers state."""
    count = 0

    def increment():
        nonlocal count  # Modify outer variable
        count += 1
        return count

    return increment

counter = make_counter()
print(counter())  # 1
print(counter())  # 2
print(counter())  # 3

Closure for Configuration

def make_multiplier(factor):
    """Create function that multiplies by factor."""
    return lambda x: x * factor

double = make_multiplier(2)
triple = make_multiplier(3)

print(double(5))  # 10
print(triple(5))  # 15

Decorators

Basic Decorator

def my_decorator(func):
    """Wrapper function."""
    def wrapper():
        print("Before function call")
        func()
        print("After function call")
    return wrapper

@my_decorator
def say_hello():
    print("Hello!")

say_hello()
# Output:
# Before function call
# Hello!
# After function call

Decorator with Arguments

def timing_decorator(func):
    """Measure execution time."""
    import time

    def wrapper(*args, **kwargs):
        start = time.time()
        result = func(*args, **kwargs)
        end = time.time()
        print(f"{func.__name__} took {end - start:.4f}s")
        return result
    return wrapper

@timing_decorator
def slow_function():
    import time
    time.sleep(1)
    return "Done"

print(slow_function())
# slow_function took 1.0001s
# Done

Decorator with Parameters

def repeat(n):
    """Decorator factory."""
    def decorator(func):
        def wrapper(*args, **kwargs):
            for _ in range(n):
                result = func(*args, **kwargs)
            return result
        return wrapper
    return decorator

@repeat(3)
def say_hello():
    print("Hello!")

say_hello()
# Hello!
# Hello!
# Hello!

Preserving Function Metadata

from functools import wraps

def logging_decorator(func):
    @wraps(func)  # Preserves name, docstring, etc.
    def wrapper(*args, **kwargs):
        print(f"Calling {func.__name__}")
        return func(*args, **kwargs)
    return wrapper

@logging_decorator
def add(a, b):
    """Add two numbers."""
    return a + b

print(add.__name__)   # "add" (not "wrapper")
print(add.__doc__)    # "Add two numbers."

Built-in Decorators

@staticmethod

class MathUtils:
    @staticmethod
    def add(a, b):
        """Doesn't access class or instance."""
        return a + b

MathUtils.add(1, 2)  # No self needed

@classmethod

class Person:
    count = 0

    @classmethod
    def get_count(cls):
        """Alternative constructor or class method."""
        return cls.count

    @classmethod
    def from_birth_year(cls, name, year):
        """Create instance from birth year."""
        age = 2024 - year
        return cls(name, age)

@property

class Circle:
    def __init__(self, radius):
        self._radius = radius

    @property
    def radius(self):
        return self._radius

    @radius.setter
    def radius(self, value):
        if value > 0:
            self._radius = value

    @property
    def area(self):
        """Computed property (read-only)."""
        return 3.14 * self._radius ** 2

itertools Module

Infinite Iterators

from itertools import count, cycle, repeat

# count() - infinite counter
counter = count(start=10, step=2)
print(next(counter))  # 10
print(next(counter))  # 12

# cycle() - repeat iterable infinitely
colors = cycle(["red", "green", "blue"])
print(next(colors))  # red
print(next(colors))  # green
print(next(colors))  # blue
print(next(colors))  # red (cycles back)

# repeat() - repeat element
ones = repeat(1, 5)
print(list(ones))     # [1, 1, 1, 1, 1]

Finite Iterators

from itertools import chain, accumulate, takewhile, dropwhile

# chain() - combine iterables
a = [1, 2, 3]
b = [4, 5, 6]
print(list(chain(a, b)))  # [1, 2, 3, 4, 5, 6]

# accumulate() - running total
print(list(accumulate([1, 2, 3, 4])))  # [1, 3, 6, 10]

# takewhile() - take while condition true
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
print(list(takewhile(lambda x: x < 5, numbers)))  # [1, 2, 3, 4]

# dropwhile() - drop while condition true
print(list(dropwhile(lambda x: x < 5, numbers)))  # [5, 6, 7, 8, 9, 10]

Combinatorics

from itertools import permutations, combinations, product

# permutations() - all orderings
print(list(permutations([1, 2, 3], 2)))
# [(1, 2), (1, 3), (2, 1), (2, 3), (3, 1), (3, 2)]

# combinations() - all combinations (order doesn't matter)
print(list(combinations([1, 2, 3], 2)))
# [(1, 2), (1, 3), (2, 3)]

# product() - Cartesian product
print(list(product([1, 2], [3, 4])))
# [(1, 3), (1, 4), (2, 3), (2, 4)]

functools Module

lru_cache (Memoization)

from functools import lru_cache

@lru_cache(maxsize=128)
def fibonacci(n):
    """Cached Fibonacci function."""
    if n < 2:
        return n
    return fibonacci(n - 1) + fibonacci(n - 2)

# Much faster with caching
print(fibonacci(100))  # 354224848179261915075
print(fibonacci.cache_info())  # Cache statistics

partial

from functools import partial

def power(base, exponent):
    return base ** exponent

square = partial(power, exponent=2)
cube = partial(power, exponent=3)

print(square(5))  # 25
print(cube(5))    # 125

reduce

from functools import reduce

numbers = [1, 2, 3, 4, 5]
product = reduce(lambda x, y: x * y, numbers)  # 120

List Comprehensions vs Functional

List Comprehension (More Pythonic)

# Map
squares = [x ** 2 for x in range(10)]

# Filter
evens = [x for x in range(10) if x % 2 == 0]

# Combined
even_squares = [x ** 2 for x in range(10) if x % 2 == 0]

Functional Style

from functools import reduce

# Map
squares = list(map(lambda x: x ** 2, range(10)))

# Filter
evens = list(filter(lambda x: x % 2 == 0, range(10)))

# Reduce
total = reduce(lambda acc, x: acc + x, numbers)

Quick Reference

Concept Syntax Description
Lambda lambda x: x * 2 Anonymous function
map map(func, iterable) Apply to all
filter filter(func, iterable) Keep matching
reduce reduce(func, iterable) Combine all
Closure Inner function captures outer State retention
Decorator @decorator Wrap function
partial partial(func, arg) Fix arguments
lru_cache @lru_cache() Memoization

Back to Resources