First-Class Functions

First-class functions are functions that can be treated like any other value. You can pass them to functions as arguments, return them from functions, and save them in variables.

Properties of first class functions:

  • A function is an instance of the Object type.
  • You can store the function in a variable.
  • You can pass the function as a parameter to another function.
  • You can return the function from a function.
  • You can store them in data structures such as hash tables, lists, …

Functions are objects

Python functions are first class objects. In the example below, we are assigning function to a variable. This assignment doesn’t call the function. It takes the function object referenced by shout and creates a second name pointing to it, yell.

# Python program to illustrate functions 
# can be treated as objects 
def shout(text): 
    return text.upper() 
  
print(shout('Hello'))
yell = shout 
print(yell('Hello'))
HELLO
HELLO

Functions can be passed as arguments to other functions:

Because functions are objects we can pass them as arguments to other functions. Functions that can accept other functions as arguments are also called higher-order functions. In the example below, we have created a function greet which takes a function as an argument.

# Python program to illustrate functions 
# can be passed as arguments to other functions 
def shout(text): 
    return text.upper() 

def whisper(text): 
    return text.lower() 

def greet(func): 
    # storing the function in a variable 
    greeting = func("Hi, I am created by a function passed as an argument.") 
    print(greeting)

greet(shout) 
greet(whisper) 
HI, I AM CREATED BY A FUNCTION PASSED AS AN ARGUMENT.
hi, i am created by a function passed as an argument.

Let's look an other example on passing a function as an argument to another function. A great example of this is the Map function. Map Function takes a function and an array as its arguments and it runs each value of that array through the provided function and then returns a new array of those results.

def my_map(func, arg_list):
    result = []
    for i in arg_list:
        result.append(func(i))
    return result

def square(x):
    return x*x

def cube(x):
    return x*x*x
squares = my_map(square, [1, 2, 3, 4, 5])
# We are not adding the parentheses for square function 
# Because adding parentheses would try to  execute the function 
print(squares)
[1, 4, 9, 16, 25]
cubes = my_map(cube, [1, 2, 3, 4, 5, 6])
print(cubes)
[1, 8, 27, 64, 125, 216]

Functions can return another function

Because functions are objects we can return a function from another function. In the below example, the create_adder function returns adder function.

# Python program to illustrate functions 
# Functions can return another function 
  
def create_adder(x): 
    def adder(y): 
        return x+y 
  
    return adder 
  
add_15 = create_adder(15) 
  
print(add_15(10))
25
def logger(msg):
    
    def log_message():
        print('Log: ', msg)
    return log_message
log_error = logger("Fatal, error")
log_error()
Log:  Fatal, error
logger("Info: Hello")()
Log:  Info: Hello

Using Return Insted of Print

def rt_logger(msg):
    
    def rt_log_message():
        return 'Log: ', msg
    return rt_log_message
rt_logger("hii")
<function __main__.rt_logger.<locals>.rt_log_message()>
# As we are returning a function with out parentheses, it won't be able to execute 
# unless the parentheses are given and called
rt_logger("hii")()
('Log: ', 'hii')
# Returning a executing function
def rt_logger(msg):
    
    def rt_log_message():
        return 'Log: ', msg
    return rt_log_message()
rt_logger("hii")
('Log: ', 'hii')
def logger_wth_args(msg):
    
    def log_message_args(log_arg):
        print("Outer Fucntion Log value: ",msg)
        print("Inner Function Log value: ", log_arg)
        print("Together: ",msg,log_arg)
        
    return log_message_args
lgr = logger_wth_args("Info:")
lgr("LGR Var created")
Outer Fucntion Log value:  Info:
Inner Function Log value:  LGR Var created
Together:  Info: LGR Var created
lgr("No Var created")
Outer Fucntion Log value:  Info:
Inner Function Log value:  No Var created
Together:  Info: No Var created

One thing important to point out here is that from the first step when we gave Info: as an argument for logger_wth_args() and till the last step when we gave No var created as an argument for returned function log_message_args. It remembered our initial message that we passed in to this initial logger funciton [Outer function]. This what called as a Closure.

def html_tag(tag):

    def wrap_text(msg):
        print('<{0}>{1}</{0}>'.format(tag, msg))

    return wrap_text

print_h1 = html_tag('h1')
print_h1('Test Headline!')
print_h1('Another Headline!')

print_p = html_tag('p')
print_p('Test Paragraph!')
<h1>Test Headline!</h1>
<h1>Another Headline!</h1>
<p>Test Paragraph!</p>