In Python, functions are first-class objects. This means that functions can be assigned to variables, passed as arguments to other functions, and returned from functions just like any other object. This feature enables the creation of higher-order functions—functions that can take other functions as arguments or return functions as results.

This powerful feature facilitates a functional programming style in Python, where functions can be composed, combined, or reused flexibly. By passing a function as an argument, you can build more generic, reusable, and modular code.


Function as Input Parameter

Let’s take a simple example of a higher-order function that merges and sorts two arrays. Instead of hard-coding a sorting algorithm, we can pass the sorting function as a parameter. This gives us the flexibility to use any sorting algorithm as needed.

Example: Higher-Order Function for Sorting Two Arrays

def sorting_two_arrays(array_0, array_1, sort_algo):
    return sort_algo(array_0 + array_1)

def quick_sort(arr):
    if len(arr) <= 1:
        return arr
    else:
        pivot = arr[len(arr) // 2]
        left = [x for x in arr if x < pivot]
        middle = [x for x in arr if x == pivot]
        right = [x for x in arr if x > pivot]
        return quick_sort(left) + middle + quick_sort(right)
        
array_0, array_1 = [5, 1, 9], [3, 7, 4]
sorted_array = sorting_two_arrays(array_0, array_1, quick_sort)
print("Sorted array:", sorted_array)

The sorting_two_arrays function takes two lists (array_0 and array_1) and a sorting algorithm (sort_algo) as parameters, merges the lists, and applies the provided sorting algorithm. Since sort_algo is a function, it can be passed as an argument, allowing the function to work with any sorting algorithm. In this example, the quick_sort function, a recursive implementation of the quicksort algorithm, is used to sort the combined list by partitioning around a pivot. The flexibility of passing quick_sort into sorting_two_arrays demonstrates Python’s ability to treat functions as objects, making the code more modular and reusable.


Partial: Bind Parameters to Function

The functools.partial() function allows you to fix a portion of a function’s arguments, creating a new function with fewer parameters. This is helpful when calling a function repeatedly with similar arguments.

Example: Pre-configuring File Writing Using partial

from functools import partial

# A function that writes a message to a file
def write_to_file(file_path, message):
    with open(file_path, 'a') as f:
        f.write(message + '\\n')

# Pre-configure the file path using partial
log_to_file = partial(write_to_file, "application.log")

# Now you can easily log messages without 
# specifying the file path every time
log_to_file("Application started")
log_to_file("An error occurred")
log_to_file("Application stopped")

The write_to_file function takes a file_path and a message to write to the specified file. By using functools.partial, we fix the file_path to "application.log", creating a new function log_to_file that only requires the message as input. This simplifies logging by allowing you to call log_to_file with just the message, eliminating the need to repeatedly specify the file path. This approach reduces redundancy, making code more concise and practical for tasks like logging, which are common in real-world applications.


Map: Apply a Function to Each Element in an Iterable

The map() function allows you to apply a given function to every item in an iterable (such as a list or tuple), producing a new iterable with the results. This is useful when you want to transform each element of a collection in a consistent way.

Example: Applying a Function to Double the Values in a List

# Using map to apply the 'quick_sort' function to two lists
array_0, array_1 = [5, 1, 9], [3, 7, 4]
sorted_arrays = list(map(quick_sort, [array_0, array_1]))

print("Sorted arrays:", sorted_arrays)  
# Output: [[1, 5, 9], [3, 4, 7]]

In this example, the map() function applies the quick_sort() function to each element (which are lists in this case) in the iterable [array_0, array_1], producing a new list of sorted arrays. The key takeaway is that map() allows you to apply a transformation (like sorting) to multiple collections without needing to explicitly loop through them.