Python November 13 ,2025

 

Python Built-in Higher-Order Functions: 

map(), filter(), and reduce()

Table of Contents

  1. Introduction to Higher-Order Functions
  2. Understanding Higher-Order Functions
  3. map() Function
  4. filter() Function
  5. reduce() Function
  6. First-Class Functions
  7. Inner (Nested) Functions
  8. Closures
  9. Real-Life Closure Examples
  10. Decorators — Concept and Usage
  11. Decorators with Arguments
  12. Practical Decorator Use Cases
  13. Multiple Decorators
  14. The functools Module
  15. functools.wraps
  16. functools.partial
  17. Anonymous (Lambda) Functions — Advanced Use
  18. Generators (yield)
  19. Asynchronous (Async) Functions
  20. Function Introspection
  21. Summary Table of Function Concepts

Introduction to Higher-Order Functions

Python is a multi-paradigm language that supports both object-oriented and functional programming styles.
One of the powerful features that come from functional programming is the concept of higher-order functions.

These functions help you write cleaner, shorter, and more expressive code when working with collections like lists, tuples, and sets.

Understanding Higher-Order Functions

A higher-order function is any function that can do one or both of the following:

  1. Accept another function as an argument, or
  2. Return another function as a result

In simple terms, higher-order functions treat functions just like data — they can be passed around, stored in variables, or used as inputs to other functions.

This concept makes your code more modular, reusable, and expressive.
Python provides several built-in higher-order functions — among them, map(), filter(), and reduce() are the most widely used.

Example:

def apply_operation(func, x, y):
    return func(x, y)

def add(a, b): return a + b
def multiply(a, b): return a * b

print(apply_operation(add, 3, 5))
print(apply_operation(multiply, 3, 5))

Output:

8
15

1. map() Function

Concept

map() applies a given function to every item of an iterable (like a list or tuple) and returns a map object, which can be converted into a list, set, or tuple.

Syntax

map(function, iterable)
  • function: The function to apply to each element.
  • iterable: A sequence (like a list) whose items will be processed.

Example 1: Basic Use of map()

numbers = [1, 2, 3, 4, 5]
squares = list(map(lambda x: x ** 2, numbers))
print(squares)

Output:

[1, 4, 9, 16, 25]

Explanation:

  • The lambda function takes one argument x and returns its square (x ** 2).
  • map() applies this function to each element in the list numbers.

Example 2: Using a Defined Function

def to_celsius(fahrenheit):
    return (fahrenheit - 32) * 5/9

temps_f = [32, 50, 68, 86]
temps_c = list(map(to_celsius, temps_f))
print(temps_c)

Output:

[0.0, 10.0, 20.0, 30.0]

Here, map() converts each Fahrenheit temperature into Celsius by applying the to_celsius function to each element.

Key Points about map()

  • It does not modify the original iterable.
  • It returns an iterator, which can be converted to a list or any other sequence type.
  • It’s best used when you want to transform all elements of a collection.

2. filter() Function

Concept

filter() filters the elements of an iterable based on a condition provided by a function.
It returns only those elements for which the function returns True.

Syntax

filter(function, iterable)
  • function: A function that returns True or False.
  • iterable: A sequence to filter.

Example 1: Filtering Even Numbers

numbers = [1, 2, 3, 4, 5, 6]
even_numbers = list(filter(lambda x: x % 2 == 0, numbers))
print(even_numbers)

Output:

[2, 4, 6]

Explanation:

  • The lambda function checks if each number is divisible by 2.
  • filter() keeps only those elements for which the condition is True.

Example 2: Filtering Strings

names = ["John", "Alice", "", "Bob", "", "Clara"]
valid_names = list(filter(lambda name: name != "", names))
print(valid_names)

Output:

['John', 'Alice', 'Bob', 'Clara']

Explanation:
This filters out empty strings, keeping only valid names.

Key Points about filter()

  • It returns an iterator (not a list directly).
  • If the function returns False, the element is excluded.
  • Ideal for selecting specific elements that meet certain conditions.

3. reduce() Function

Concept

reduce() is not a built-in function by default; it’s available in the functools module.
It reduces an iterable into a single cumulative value by applying a function cumulatively to its elements.

Syntax

from functools import reduce
reduce(function, iterable[, initializer])
  • function: A function that takes two arguments.
  • iterable: Sequence to reduce.
  • initializer (optional): Starting value (if provided).

Example 1: Multiplying All Numbers

from functools import reduce

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

Output:

24

Explanation:

  • First, 1 * 2 = 2
  • Then, 2 * 3 = 6
  • Finally, 6 * 4 = 24
    reduce() applies the lambda function cumulatively across the list.

Example 2: Summing Numbers with an Initial Value

from functools import reduce

numbers = [10, 20, 30]
total = reduce(lambda x, y: x + y, numbers, 100)
print(total)

Output:

160

Here, the initial value 100 is added before the reduction starts.

Example 3: Finding the Maximum Element

from functools import reduce

numbers = [3, 8, 2, 10, 5]
max_num = reduce(lambda x, y: x if x > y else y, numbers)
print(max_num)

Output:

10

Key Points about reduce()

  • Always import it from functools.
  • It reduces an iterable into a single result (sum, product, max, etc.).
  • Best used when an operation needs to accumulate a single outcome.

Comparison Summary

FunctionPurposeInputOutputTypical Use
map()Applies a function to all elementsfunction + iterableTransformed iterableData transformation
filter()Keeps elements that meet a conditionfunction + iterableFiltered iterableData selection
reduce()Combines all elements into onefunction + iterableSingle valueAggregation

Practical Example: Combining All Three

from functools import reduce

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

# Step 1: Square all numbers
squares = list(map(lambda x: x ** 2, numbers))

# Step 2: Filter squares greater than 10
filtered = list(filter(lambda x: x > 10, squares))

# Step 3: Find the sum of remaining numbers
result = reduce(lambda x, y: x + y, filtered)

print("Squares:", squares)
print("Filtered:", filtered)
print("Sum:", result)

Output:

Squares: [1, 4, 9, 16, 25, 36]
Filtered: [16, 25, 36]
Sum: 77

This shows how map(), filter(), and reduce() can be combined for efficient data processing.

 Advanced Function Concepts in Python

1. First-Class Functions

In Python, functions are first-class objects.
That means they can be:

  • Assigned to variables
  • Stored in data structures
  • Passed as arguments to other functions
  • Returned from other functions

This makes Python functions very flexible.

Example: Assigning and Passing Functions

def greet(name):
    return "Hello " + name

say_hello = greet  # assign function to variable
print(say_hello("Alice"))

def call_func(func):
    print(func("Bob"))

call_func(greet)

Output:

Hello Alice
Hello Bob

Here, greet is treated like any other object — passed and returned freely.

2. Inner (Nested) Functions

Python allows you to define a function inside another function.
This is useful for encapsulating logic that should only be used inside a specific scope.

Example:

def outer_function():
    def inner_function():
        print("Inner function executed")
    inner_function()

outer_function()

Output:

Inner function executed

Here, inner_function() exists only inside outer_function().

3. Closures

A closure is a function that remembers the values of variables from its enclosing scope, even after that scope is gone.

This happens when:

  1. You define a function inside another function, and
  2. The inner function refers to variables from the outer function.

Example:

def outer(msg):
    def inner():
        print("Message:", msg)
    return inner  # returning function, not calling it

say_hello = outer("Hello World")
say_hello()

Output:

Message: Hello World

Explanation:

  • outer() returns inner() function.
  • Even though outer() finished execution, inner() still “remembers” msg = "Hello World".
    This is a closure — it captures variables from its outer environment.

Real-life Example of a Closure

Closures are often used for configuration or function factories.

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

square = power(2)
cube = power(3)

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

Output:

25
125

Here, square and cube “remember” their exponent values independently.

4. Decorators (In-Depth)

A decorator is a special function that modifies the behavior of another function without changing its source code.

They are built using:

  • Higher-order functions (functions returning functions)
  • Closures

Syntax:

@decorator_name
def function_name():
    ...

is equivalent to:

function_name = decorator_name(function_name)

Example 1: Basic Decorator

def decorator(func):
    def wrapper():
        print("Before function call")
        func()
        print("After function call")
    return wrapper

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

say_hello()

Output:

Before function call
Hello!
After function call

Example 2: Decorator with Arguments

def decorator(func):
    def wrapper(*args, **kwargs):
        print("Function is being called with arguments:", args)
        return func(*args, **kwargs)
    return wrapper

@decorator
def add(a, b):
    return a + b

print(add(5, 3))

Output:

Function is being called with arguments: (5, 3)
8

Example 3: Practical Use — Logging or Authentication

def login_required(func):
    def wrapper(username):
        if username == "admin":
            return func(username)
        else:
            print("Access Denied")
    return wrapper

@login_required
def dashboard(user):
    print(f"Welcome to the dashboard, {user}")

dashboard("guest")
dashboard("admin")

Output:

Access Denied
Welcome to the dashboard, admin

Example 4: Multiple Decorators

You can stack decorators by placing multiple @decorator lines.

def bold(func):
    def wrapper():
        return "" + func() + ""
    return wrapper

def italic(func):
    def wrapper():
        return "" + func() + ""
    return wrapper

@bold
@italic
def greet():
    return "Hello"

print(greet())

Output:

Hello

5. The functools Module — 

functools is a Python module that provides higher-order function utilities — meaning functions that operate on other functions.

Two of the most important tools:

  1. functools.wraps – for decorators
  2. functools.partial – for creating partially applied functions

Both are core functional programming concepts.

(A) @functools.wraps — In-Depth Explanation

Why decorators break metadata?

When you apply a decorator:

@decorator
def say_hi():
    ...

Python does something like this internally:

say_hi = decorator(say_hi)

Inside the decorator:

def wrapper():
    ...
return wrapper

So the original function is replaced by wrapper.

Therefore, the decorated function loses:

  • __name__
  • __doc__
  • __module__
  • __annotations__
  • __signature__

Without wraps, this happens:

def decorator(func):
    def wrapper():
        func()
    return wrapper

@decorator
def say_hi():
    """Says hi."""
    print("Hi")

print(say_hi.__name__)  # wrapper (incorrect)
print(say_hi.__doc__)   # None (lost)

This becomes a big problem for:

  • debugging
  • introspection
  • documentation generators
  • IDE autocomplete
  • unit test tools
  • decorators stacked on decorators

Role of @functools.wraps

wraps is actually a decorator that returns another decorator.

Conceptually:

wraps(original_function) → decorator_that_updates_metadata

This second decorator then applies to the wrapper function.

So:

@wraps(func)
def wrapper():
    ...

means:
“Copy the metadata of func into this wrapper.”

What metadata does wraps copy?

wraps copies:

  • __name__
  • __doc__
  • __module__
  • __annotations__
  • __qualname__
  • __wrapped__ (very important!)
  • Attributes listed in functools.WRAPPER_ASSIGNMENTS

Internally:

wrapper.__wrapped__ = func
wrapper.__name__ = func.__name__
wrapper.__doc__ = func.__doc__
...

__wrapped__ is especially important because:

  • inspect module uses it
  • help() uses it
  • tools like functools.lru_cache depend on it

Correct decorator with wraps

from functools import wraps

def decorator(func):
    @wraps(func)
    def wrapper():
        print("Wrapped")
        return func()
    return wrapper

Now metadata is preserved.

Why wraps is essential (real-life reasons)

✔ A. Debugging becomes clearer

Error messages show the real function, not “wrapper”.

✔ B. IDEs and linters identify functions correctly

✔ C. Multiple decorators stack cleanly

Without __wrapped__, stacking decorators gets messy.

✔ D. Tools like Flask, FastAPI depend on metadata

Routing and dependency injection use function names and docs.

✔ E. Testing frameworks like pytest rely on function metadata.

 Example: decorator without wraps vs with wraps

Without wraps

def log(func):
    def wrapper(*args, **kwargs):
        print("Calling", func.__name__)
        return func(*args, **kwargs)
    return wrapper

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

Now:

add.__name__  # "wrapper"
add.__doc__   # None

With wraps

from functools import wraps

def log(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        print("Calling", func.__name__)
        return func(*args, **kwargs)
    return wrapper

Now:

add.__name__  # "add"
add.__doc__   # "Add two numbers"

(B) functools.partial —

The idea

partial() allows you to fix some arguments in advance and create a new function with fewer required parameters.

This is called:

  • partial function application
  • argument binding
  • currying (related concept)

 Basic Example

from functools import partial

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

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

Now:

square(5) → power(5, 2)
cube(4) → power(4, 3)

How partial actually works internally

When you create:

square = partial(power, exponent=2)

partial stores:

func = power
fixed positional args   = ()
fixed keyword args      = {"exponent": 2}

Calling:

square(10)

is equivalent to:

power(10, exponent=2)

Partial behaves almost like:

def square(x):
    return power(x, exponent=2)

But it also supports advanced combinations of positional + keyword arguments.

Inspecting a partial function

print(square.func)       # original function
print(square.args)       # fixed positional arguments
print(square.keywords)   # fixed keyword args

Why use partial?

✔ A. Turn general functions into specialized versions

A very generic function can become many specific functions.

✔ B. Cleaner code

Avoid passing the same parameters repeatedly.

✔ C. Useful for callbacks

GUIs, event handlers, schedulers often need callback functions with zero arguments.
partial allows you to pass extra parameters easily.

✔ D. Useful in functional programming pipelines

6. Anonymous (Lambda) Functions — Advanced Use

Lambdas are short, unnamed functions often used in functional programming style.

They can be used with:

  • map()
  • filter()
  • reduce()
  • Sorting with custom keys

Example: Sorting with lambda

students = [("Alice", 25), ("Bob", 20), ("Charlie", 23)]
sorted_students = sorted(students, key=lambda x: x[1])
print(sorted_students)

Output:

[('Bob', 20), ('Charlie', 23), ('Alice', 25)]

7. Generators (Functions that Yield)

A generator is a special kind of function that yields values one at a time, instead of returning them all at once.

They are memory-efficient because they don’t store the entire result in memory.

Example:

def countdown(n):
    while n > 0:
        yield n
        n -= 1

for num in countdown(5):
    print(num)

Output:

5
4
3
2
1

Here, yield pauses the function and resumes when next value is requested.

8. Asynchronous (Async) Functions

Python supports asynchronous programming using async and await for non-blocking execution.

Example:

import asyncio

async def greet():
    print("Hello")
    await asyncio.sleep(1)
    print("World")

asyncio.run(greet())

Output:

Hello
World

Here, await pauses execution while waiting for asynchronous tasks to finish, allowing other code to run concurrently.

9. Function Introspection

You can inspect function attributes using built-in attributes like:

  • __name__
  • __doc__
  • __defaults__
  • __code__.co_varnames
  • __annotations__

Example:

def sample(a: int, b: int = 5) -> int:
    """Adds two numbers"""
    return a + b

print(sample.__name__)
print(sample.__doc__)
print(sample.__defaults__)
print(sample.__annotations__)
print(sample.__code__.co_varnames)

Output:

sample
Adds two numbers
(5,)
{'a': , 'b': , 'return': }
('a', 'b')

10. Summary Table

ConceptDescriptionExample
ClosureInner function remembering variablesFunction returning another function
DecoratorModifies another function@decorator_name
PartialFix some argumentspartial(func, arg=value)
LambdaAnonymous inline functionlambda x: x + 1
GeneratorFunction that yields sequenceUses yield
Async FunctionNon-blocking async operationUses async / await

 

Next Blog- Modules and Packages in Python

Sanjiv
0

You must logged in to post comments.

Get In Touch

G06, Kristal Olivine Bellandur near Bangalore Central Mall, Bangalore Karnataka, 560103

+91-8076082435

techiefreak87@gmail.com