Chapter 11

Functional Programming in Python

13.0 Intro 13.1 Functional Concepts 13.2 Functional Practice

Use ← → arrow keys or Space to navigate  |  Press F for fullscreen

What is Functional Programming?

Writing programs as transformations from inputs to outputs

Why Functional Programming?

Imperative approach

Describe each step and update state along the way.

prices = [10, 20, 30]
discounted = []

for price in prices:
    new_price = price * 0.9
    discounted.append(new_price)

Functional approach

Describe the transformation and produce a new value.

prices = [10, 20, 30]

discounted = [
    price * 0.9
    for price in prices
]
FP benefits: smaller functions, fewer side effects, easier testing, and clearer data pipelines.

Functional Vocabulary

Pure functionSame input gives same output; no external changes.
ImmutabilityReturn new values instead of changing old ones.
First-class functionPass functions around like any other value.
Higher-order functionTakes a function or returns a function.
LambdaSmall anonymous function written inline.
DecoratorWraps a function with reusable behavior.

13.1 Functional Concepts

Purity, immutability, functions as values, lambdas, decorators, comprehensions

Pure Functions

  • Depend only on their arguments.
  • Return a value instead of modifying external state.
  • Are easier to test because they are predictable.
Same input → same output. No hidden reads or writes.
# Pure
def add_tax(price, rate):
    return price * (1 + rate)

# Not pure: depends on global state
tax_rate = 0.07

def add_tax_global(price):
    return price * (1 + tax_rate)

Immutability

Functional code often returns a new value instead of changing an existing one.

  • Original data stays available.
  • Unexpected side effects are less likely.
  • Pipelines become easier to reason about.
def add_item(items, new_item):
    return items + [new_item]

cart = ["book", "pen"]
new_cart = add_item(cart, "notebook")

print(cart)      # ['book', 'pen']
print(new_cart)  # ['book', 'pen', 'notebook']

Functions as Values

  • Assign a function to a variable.
  • Pass a function as an argument.
  • Return a function from another function.
This is the foundation for map, filter, decorators, callbacks, and custom sorting.
def square(x):
    return x * x

def apply(func, values):
    return [func(v) for v in values]

nums = [1, 2, 3, 4]
print(apply(square, nums))
# [1, 4, 9, 16]

Lambda Functions

A lambda is a small anonymous function for simple expressions.

Use caseExample
Sort keykey=lambda s: len(s)
Quick transformlambda x: x * 2
Pair keylambda item: item[1]
If the logic needs multiple steps, use def.
names = ["Ada", "Grace", "Linus"]

by_length = sorted(
    names,
    key=lambda name: len(name)
)

print(by_length)
# ['Ada', 'Grace', 'Linus']

Map, Filter, and Comprehensions

Functional tools

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

squares = list(map(lambda n: n*n, nums))
evens = list(filter(lambda n: n % 2 == 0, nums))

Pythonic alternative

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

squares = [n*n for n in nums]
evens = [n for n in nums if n % 2 == 0]
In Python, comprehensions are often clearer than map and filter with lambdas.

Higher-Order Functions

A higher-order function works with other functions.

  • Takes a function as input.
  • Returns a function as output.
  • Builds reusable behavior without duplicating control flow.
def make_multiplier(factor):
    def multiply(x):
        return x * factor
    return multiply

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

print(double(10))  # 20
print(triple(10))  # 30

Decorators

  • A decorator takes a function and returns a wrapped function.
  • Use it for logging, timing, validation, caching, and access checks.
  • @decorator syntax replaces manual wrapping.
def announce(func):
    def wrapper(*args, **kwargs):
        print(f"Calling {func.__name__}")
        return func(*args, **kwargs)
    return wrapper

@announce
def greet(name):
    return f"Hello, {name}"

print(greet("Ada"))

13.2 Functional Practice

Recursion, context managers, and practical tools from functools

Recursion

  • A recursive function calls itself.
  • Every recursive function needs a base case.
  • Good fit for problems with repeated self-similar structure.
Recursion is elegant for some problems, but iteration is often simpler in Python.
def factorial(n):
    if n == 0:          # base case
        return 1
    return n * factorial(n - 1)

print(factorial(5))     # 120

Context Managers

A context manager handles setup and cleanup around a block of code.

  • Open and close files safely.
  • Acquire and release resources.
  • Keep cleanup reliable even when errors happen.
from contextlib import contextmanager

@contextmanager
def section(name):
    print(f"Start {name}")
    try:
        yield
    finally:
        print(f"End {name}")

with section("demo"):
    print("working")

The functools Module

ToolUse
reduceCombine many values into one
partialPre-fill some function arguments
lru_cacheMemoize expensive function calls
Use these when they simplify the code. Do not force them into every problem.
from functools import reduce, partial, lru_cache

total = reduce(lambda a, b: a + b, [1, 2, 3])

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

square = partial(power, exp=2)

@lru_cache
def fib(n):
    if n < 2:
        return n
    return fib(n-1) + fib(n-2)

Choosing a Style

Use functional style when...

  • You are transforming collections.
  • A pure helper function makes logic easier to test.
  • A comprehension or small pipeline is clearer than a loop.

Prefer another style when...

  • The lambda becomes hard to read.
  • The code needs several statements or error handling.
  • Mutating state is the simplest honest model.
Python supports multiple styles. The goal is readable, testable code, not functional purity.

Chapter 11 - Quick Reference

ConceptKey syntax / notes
Pure functionNo side effects; depends only on arguments
Immutable updateReturn a new value instead of mutating the original
Lambdalambda args: expression
Map/filtermap(func, xs), filter(pred, xs)
Comprehension[expr for item in xs if condition]
Decorator@decorator wraps a function
RecursionBase case plus recursive case
Context managerwith manager: handles setup and cleanup
functoolsreduce, partial, lru_cache

End of Chapter 11

Next up: DSA and algorithms

pure functions · lambdas · decorators · recursion · functools