Python November 13 ,2025

Table of Contents

  • 5. 2. filter() Function
  • 6. 3. reduce() Function
  • 7. Comparison Summary
  • 8. Practical Example: Combining All Three
  • 9. Advanced Function Concepts in Python
  •  

    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

    Kurki bazar Uttar Pradesh

    +91-8808946970

    techiefreak87@gmail.com