Skip to main content
Python

Python Functions are First-Class Objects: How to Assign, Pass, and Return Them

4 mins

LEGO building blocks rationale show lego pieces that are stored in different containers to represent how Python functions can be assigned to variables.

Everything in Python is an Object #

In Python, everything is an object. This includes integers, strings, lists, and also functions. Just like any other object, functions can be assigned to variables, passed as arguments to other functions, and returned from other functions. This is known as first-class functions and makes Python a flexible programming language.

Functions have a Type #

In Python, functions have a type just like any other object. You can use the type() function to determine the type of any object including functions. For example:

def greet(name):
    return f'Hello, {name}!'

print(type(greet))  # <class 'function'>

Functions have a memory address #

Since functions are objects in Python, they have a memory address like objects. Therefore, you can use the id() function to get the memory address of a function.

print(id(greet))  # 124521020089224

Assigning Functions to Variables #

Assign functions to variables in Python by using the function name without parentheses. This creates a reference to the function that can be called later using the variable name with parentheses.

hello = greet
print(hello('Alice'))  # Hello, Alice!

note that the memory address of the function is the same as the memory address of the variable that references the function.

print(id(hello))  # 124521020089224
print(id(greet))  # 124521020089224

Higher-Order Functions #

A higher-order function is a function that takes another function as an argument or returns a function as a result, or both.

Passing Functions as Arguments #

To pass a function as an argument to another function, simply use the function name without parentheses. This allows you to pass the function as a reference to be called later.

Here is an example of a higher-order function that takes a function as an argument:

def print_message(func, message):
    print(func(message))   

print_message(greet, 'Alice')  # Hello, Alice!

But what if the funcation that is passed as an argument does not have the expected number of arguments? For example, what if the function that is passed as an argument does not take any arguments, but the higher-order function expects the function to take an argument?

In this case, you will get a TypeError when you try to call the function that was passed as an argument with an argument.

def print_message(func, message):
    print(func(message))

def no_argument_func():
    return 'Hello, World!'

print_message(no_argument_func, 'Alice')  # TypeError: no_argument_func() takes 0 positional arguments but 1 was given

Returning Functions from Functions #

Functions can also return other functions. This allows you to create functions that generate other functions based on certain conditions or parameters.

Here is an example of a higher-order function that returns a function:

def create_greeter():
    def greet(name):
        return f'Hello, {name}!'
    return greet

greeter = create_greeter()
print(greeter('Alice'))  # Hello, Alice!

lambda Functions #

Lambda functions are a way to create anonymous functions in Python. They are useful for creating simple functions that are only used once and do not require a name.

The format of a lambda function is lambda arguments: expression. Here is an example of a lambda function that adds two numbers:

add = lambda x, y: x + y
print(add(2, 3))  # 5

lambda functions are still functions.

The variable add now references a function and has a memory address like any other function.

Python 3.10.15 (main, Oct  3 2024, 07:27:34) [GCC 11.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> add = lambda x,y: x + y
>>> add(3,4)
7
>>> id(add)
124521020059232
>>> type(add)
<class 'function'>
>>> 

A lambda function can be used anywhere a regular function is expected, such as in a higher-order function:

def apply_operation(operation, x, y):
    return operation(x, y)

result = apply_operation(lambda x, y: x * y, 2, 3)
print(result)  # 6

Replace if Statements with Functions #

One common use case for returning functions from functions is to replace if statements with functions. This can make your code more readable and maintainable by separating different behaviors into separate functions.

Here is an example of replacing an if statement with functions stored in a dictionary:


lang = {'en': lambda name: f"Hello, {name}!", 
        'es': lambda name: f"Hola, {name}!", 
        'fr': lambda name: f"Bonjour, {name}!"}

def greet(name, lang_code):
    return lang[lang_code](name)

print(greet("John", "en"))

print(greet("John", "es"))

print(greet("John", "fr"))

outputs:

Hello, John!
Hola, John!
Bonjour, John!