Higher Order Functions - How To Create Code, That Uses Code...
HOF is all about knowing how functions are able to intertwine and use information from each other. As an experienced programmer we'll all want the ability to be able to know how to call on other functions, modify them, and use them as inputs for other functions.
Today, I’ll be revisiting the topic of HOF in Python and what we can do with them. The frameworks learnt using HOF enable programmers to create new code from old code, which is useful when we begin to think about debugging or adding functionality.
I want to remind readers and myself at this point in the course that I am learning this to write about them and gain a deeper understanding of them. This will help solidify the concepts and their uses in my mind.
Higher Order Functions (HOFs)
Simply put, a HOF is a function that takes another function as an input, returns a function, or does both. This is what makes our code more modular by taking a function and using it like variables.
Example 1: Passing a Function as Input
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# Our initial function:
# Returns the sum of the numbers provided as arguments
def sum_numbers(num):
return sum(num) # This does the calculation to return the sum
# Our HOF:
def hof(func, lst):
# Given it 2 parameters, one for a function, the other a normal input
sum = func(lst) # Used the same syntax as using a normal function, this just helps the code know that it's a function.
return sum # This uses the function on the list
result = hof(sum_numbers, [1,2,3,4,5])
print(result) # 15
Taken from the course
This example should show how we have our initial function, sum_numbers(), be used as an input to our hof() function. The HOF function doesn’t calculate or perform any tasks; it uses another function as a parameter and performs a task using the code inside it to then generate an output.
Example 2: Returning a Function
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# Some simple functions
def square(x):
return x ** 2
def cube(x):
return x ** 3
def absolute(x):
return x if x >= 0 else -x
# HOF that returns one of the above functions
def higher_order_function(func_name):
if func_name == 'square':
return square
elif func_name == 'cube':
return cube
elif func_name == 'absolute':
return absolute
result = higher_order_function('square')
print(result(3)) # 9
result = higher_order_function('cube')
print(result(3)) # 27
result = higher_order_function('absolute')
print(result(-3)) # 3
Taken from the course
This is an example to show that we can instead use HOF to have it return other functions as a result. It helps to think of it like a relational database, one HOF to many functions and many output functions, many to many.
Python Closures
Python closures are the term used to describe the use of nesting functions that can use the parameters of each other as arguments.
Hint: Parameters are placeholders for input fields in a function. Arguments are the input values replacing each parameter on a run of the program.
1
2
3
4
5
6
7
def outer(x): # parameter A
def inner(y): # parameter B
return x + y # Both used inside inner function
return inner # Outer function returns inner value
add_five = outer(5)
print(add_five(3)) # 8
Here, inner uses x from outer, even after outer has finished running. This is the foundation for decorators.
Python Decorators
Decorators allow us to add new functions to an existing function without changing the original functions code. Think of using a HOF but with a different syntax, as it helps make code clearer and concise.
- Takes a function as input.
- Wraps it inside another function.
- Returns the new function with added behaviour from the old function.
Example: Adding a Message Before a Function
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# Without a Decorator:
def greet():
print("Hello!")
def farewell():
print("Goodbye!")
# Extra behaviour repeated manually
print("Start of greet()")
greet()
print("Start of farewell()")
farewell()
# Start of greeting()
# Hello!
# Start of farewell()
# Goodbye!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#With a Decorator:
# Step 1: Create a decorator
def start_message(func):
def wrapper():
print("Start of function")
func()
return wrapper
# Step 2: Apply it - Use a new function as input
@start_message
def greet():
print("Hello!")
@start_message
def farewell():
print("Goodbye!")
# Step 3: Call on the new functions and see the new behaviour
greet()
farewell()
# Start of function # recursively added for each function.
# Hello!
# Start of function
# Goodbye!
With a decorator, we can add a recursive code to our existing code. Just think of it using an older functions return values inside the new function you build. This prevents the need for repeating code.
Key Points & Summary
This has been a mind opener to say the least, I’ve had to use a lot of AI to break the topic down into chunks that are able to make more sense out of what I was learning. The thing with learning using online sources is that you only are able to digest the information relayed and not the experience needed to fully understand concepts until you put them to use.
AI is able to help bridge that gap for me as I go on to self study, this is especially beneficial when it comes to having examples to use or practise building code. In a usual classroom environment with a lecturer you have the information and then tasks to use to complete it and understand the topic.
To summarise:
- HOFs: Functions that take other functions as input or return functions.
- Closures: Nested functions that remember variables from the outer function.
- Decorators: HOFs that modify or extend functions cleanly.
Think of a decorator as a wrapper that enhances your function while keeping the original logic intact.
