Python High Order Function

In Python, a higher-order function is a function that takes one or more functions as arguments, or returns a function as its result. These functions are used to build more complex functions and are an important tool for functional programming in Python.

Here are some examples of higher-order functions in Python:

  1. Map: The map() function takes a function and a sequence as arguments and returns a new sequence in which each element is the result of applying the function to the corresponding element of the original sequence. For example:
def square(x):
    return x ** 2

numbers = [1, 2, 3, 4, 5]
squares = map(square, numbers)
print(list(squares))  # Output: [1, 4, 9, 16, 25]
  1. Filter: The filter() function takes a function and a sequence as arguments and returns a new sequence containing only the elements for which the function returns True. For example:
def is_even(x):
    return x % 2 == 0

numbers = [1, 2, 3, 4, 5, 6]
evens = filter(is_even, numbers)
print(list(evens))  # Output: [2, 4, 6]
  1. Reduce: The reduce() function takes a function and a sequence as arguments and returns a single value that is the result of applying the function to the elements of the sequence, in a cumulative way. For example:
from functools import reduce

def add(x, y):
    return x + y

numbers = [1, 2, 3, 4, 5]
total = reduce(add, numbers)
print(total)  # Output: 15
  1. Lambda Functions: Lambda functions are anonymous functions that can be defined on the fly and passed to other functions as arguments. For example:
numbers = [1, 2, 3, 4, 5]
squares = map(lambda x: x ** 2, numbers)
print(list(squares))  # Output: [1, 4, 9, 16, 25]
  1. Decorators: A decorator is a higher-order function that takes a function as input and returns a new function as output. The new function can add some behavior to the original function without modifying its code. For example:
def my_decorator(func):
    def wrapper():
        print("Before the function is called.")
        func()
        print("After the function is called.")
    return wrapper

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

say_hello()  # Output: Before the function is called. Hello! After the function is called.

Properties of High order functions in Python:

Here are some of the key properties of higher-order functions in Python:

  1. Functions as arguments: Higher-order functions can take other functions as arguments. This allows functions to be more flexible and reusable, as the behavior of the function can be modified by passing in different functions as arguments.
  2. Functions as return values: Higher-order functions can also return functions as their result. This can be useful when creating functions that need to be customized based on the inputs or other factors.
  3. Anonymous functions: In Python, anonymous functions can be created using lambda expressions. These functions are not named and can be passed as arguments or returned by higher-order functions.
  4. Partial functions: Higher-order functions can also be used to create partial functions. This allows a function to be partially applied with arguments, creating a new function that takes fewer arguments than the original.
  5. Function composition: Higher-order functions can be used to compose multiple functions together. This can be useful for creating more complex functions that perform multiple operations on their input data.

Overall, higher-order functions are a powerful tool in Python that allow for more flexible and modular code. By taking advantage of these properties, developers can create more reusable, efficient, and maintainable code.

Method 1: Using functions as objects in High order function:

In Python, functions are first-class objects, which means they can be treated like any other object in the language. This means they can be passed as arguments to other functions and returned as values from functions. Here is an example of using functions as objects in a higher-order function:

def apply_function(func, arg):
    return func(arg)

def square(x):
    return x ** 2

result = apply_function(square, 5)
print(result)  # Output: 25

In this example, we define a function called apply_function that takes two arguments: func and arg. The func argument is a function, and the arg argument is the input value to be passed to the function. Inside the apply_function function, we simply call func with arg as its argument and return the result.

We also define a function called square that takes one argument and returns its square. We then pass square and the value 5 to apply_function, which calls square(5) and returns the result, which is 25.

This is a simple example, but it illustrates the basic idea of using functions as objects in higher-order functions. By passing functions as arguments to other functions, we can create more flexible and reusable code.

Method 2: Functions as a parameter for another function:

In Python, it is common to pass functions as parameters to another function. This is one of the key features of higher-order functions. Here is an example of a higher-order function that takes a function as a parameter:

def operate_on_list(func, lst):
    result = []
    for item in lst:
        result.append(func(item))
    return result

def square(x):
    return x ** 2

numbers = [1, 2, 3, 4, 5]
squared_numbers = operate_on_list(square, numbers)
print(squared_numbers)  # Output: [1, 4, 9, 16, 25]

In this example, we define a function called operate_on_list that takes two arguments: func and lst. The func argument is a function that will be applied to each element in the list lst. Inside the function, we loop over each item in lst, apply func to it, and append the result to a new list called result. Finally, we return the result list.

We also define a function called square that takes one argument and returns its square. We then pass square and a list of numbers to operate_on_list, which applies square to each number in the list and returns a new list of the squared numbers.

This example demonstrates how functions can be passed as parameters to other functions to create more flexible and reusable code. By abstracting the operation that is applied to each element of the list into a separate function, we can easily change the behavior of operate_on_list by passing in a different function.

Method 3: Returning function as a result in high order function:

In Python, higher-order functions can also return functions as their result. Here is an example of a higher-order function that returns a function:

def create_multiplier(x):
    def multiplier(y):
        return x * y
    return multiplier

double = create_multiplier(2)
triple = create_multiplier(3)

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

In this example, we define a function called create_multiplier that takes one argument x. Inside the function, we define another function called multiplier that takes one argument y. The multiplier function returns the product of x and y.

Finally, the create_multiplier function returns the multiplier function. We then call create_multiplier twice to create two new functions: double and triple. The double function multiplies its input by 2, and the triple function multiplies its input by 3.

We can then call double(5) and triple(5) to get the results 10 and 15, respectively.

This example demonstrates how higher-order functions can be used to create new functions that have specific behaviors. By returning a function from create_multiplier, we can create new functions with different multipliers without having to define a separate function for each multiplier.

Method 4: Decorators as high order function:

Decorators are a common example of higher-order functions in Python. A decorator is a function that takes another function as its argument and returns a new function that wraps the original function with some additional functionality. Here is an example of a decorator:

def uppercase_decorator(func):
    def wrapper(text):
        original_result = func(text)
        modified_result = original_result.upper()
        return modified_result
    return wrapper

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

print(greet("John"))  # Output: HELLO, JOHN!

In this example, we define a decorator function called uppercase_decorator that takes another function func as its argument. Inside the decorator function, we define a new function called wrapper that takes one argument text. The wrapper function calls the original func with the text argument, stores the result in a variable called original_result, converts the original_result to uppercase, and returns the modified result.

We then use the @uppercase_decorator syntax to apply the decorator to the greet function. This means that when we call greet, the uppercase_decorator function will be called with greet as its argument, and the returned function (wrapper) will be used instead of the original greet function.

When we call greet("John"), the output will be "HELLO, JOHN!". This is because the uppercase_decorator has modified the output of the original greet function by converting it to uppercase.

This example demonstrates how decorators can be used to modify the behavior of functions without changing their code. By passing a function as an argument to a decorator, we can create a new function that has the same behavior as the original function with some additional functionality.