How to work with lambda function in Python?

How to work with lambda function in Python?

# Addition using lambda function
add = (lambda x: x + 7)(3)

print('Output is', add)

Output:

Single expression:

Lambda functions can have only ONE expression, meaning that we can execute only one operation in one lambda function. We spread the expression over several lines of code using parentheses, but it will remain a single expression. Let’s see it in the example below:

# Single expression, which is in two lines, but still single
single = (lambda x:
(x % 2 and 'odd' or 'even'))(3)

print('3 is',single)

Output:

IIFE:

IIFE is immediately invoked function execution, it’s a function that runs as soon as it has been defined. Let’s see the example of it:

# lamda function here immediately returns us x in the power of x
iife = (lambda x: x ** x)(2)

print('lambda function returns -',iife)

Output:

Notice that we didn’t called the lambda function, we just defined it and it was executed.

Comparing lambda and usual functions #

In this part of the tutorial, we will break down the main differences between lambda functions and regular Python functions according to several criteria such as functions, tracebacks, arguments, and so on.

Functions:

Let’s start with the verifying and comparing how Python sees a function with a return statement and function with lambda. For these reasons we’re going to use dis module which will allow us analyze Python bytecode generated by the compiler. First let’s apply it to the lambda function.

# Importing dis module
import dis

# Calling lambda function
add = lambda x, y: x + y

# Finding class type of the argument 'add'
type(add)

# Applying dis method 
dis.dis(add)

print(add)

Output:

Now let’s analyze Python bytecode of the usual function object to compare results.

# Importing dis module
import dis

# Calling function regular function
def add(x, y): 
    return x + y

# Finding class type of the argument 'add'
type(add)

# Applying dis method 
dis.dis(add)

print(add)

Output:

Python interpreted the bytecode the same for both functions, but the names of functions are different: the function name is “add” for a function defined with def, while the lambda function is seen as “lambda” in the result.

Arguments:

Python lambda expressions accept all of the many ways of giving arguments, just like a standard function object constructed with def. This includes the following:

  • Positional arguments
  • Named arguments (keyword arguments)
  • Variable list of arguments (or varargs)
  • Variable list of keyword arguments
  • Keyword-only arguments

The following examples show the many options for providing arguments to lambda expressions:

# Summing of positional arguments
arg1 = (lambda x, y, z: x + y + z)(1, 2, 3)

print('Output of the arg1 is',arg1)

# Summing of named arguments
arg2 = (lambda x, y, z=3: x + y + z)(1, 2)

print('Output of the arg2 is',arg2)

# Summing of mixed arguments, positional and named 
arg3 = (lambda x, y, z=3: x + y + z)(1, y=2)

print('Output of the arg3 is',arg3)

# Performing sum operation on the vararg "*args"
arg4 = (lambda *args: sum(args))(1,2,3)

print('Output of the arg4 is',arg4)

# Performing sum operation on the variable list of keyword arguments
arg5 = (lambda **kwargs: sum(kwargs.values()))(one=1, two=2, three=3)

print('Output of the arg5 is',arg5)

# Performing sum operation on the keyword-only argument
arg6 = (lambda x, *, y=0, z=0: x + y + z)(1, y=2, z=3)

print('Output of the arg6 is',arg6)

Output:

In the output above we can see that nothing really changed despite that we used many different ways of giving arguments.

Closure:

A closure function is a nested function that has access to a free variable, except parameters, from the enclosing scope of the function defining the environment in which they run and can therefore be called from anywhere. Here we will see examples of the closure constructed with lambda and regular functions. First, let’s start with the normal Python function with closure:

# Defining a nested function for summing the three arguments
def out_func(x):         # x argument is for the outer function
    y = 10               # y is a local variable to the outer function
    def inn_func(z):     # z argument for the inner function
        print(f"x = {x}, y = {y}, z = {z}")
        return x + y + z
    return inn_func

# Iterating 5 times for loop
for i in range(5):
    closure = out_func(i)
    print(f"closure({i}) = {closure(i)}")

Output:

Now let’s see closure with lambda function:

# Defining a nested function for summing the three arguments
def outer_func(x):                  # x argument is for the outer function
    y = 10                          # y is a local variable to the outer function
    return lambda z: x + y + z      # z argument for the inner function

# Iterating 5 times for loop
for i in range(5):
    closure = outer_func(i)
    print(f"closure({i}) = {closure(i)}")

Output:

So, in this example, both the regular def function and lambda function behave similarly.

Evaluation time:

In some situations with loops, the behavior of a Python lambda function as a closure can be unpredictable and illogical. The following examples demonstrate the difference between using a regular function and a Python lambda function. First, let’s take a look at the case with a regular function:

# Defining a nested function
def main(n):
    def f():
        print(n)
    return f

# Assigning the values
nums = 'one', 'two', 'three'

# Creating an empty list
func = []

# Using a for loop and applying "append" method to our function
for n in numbers:
    func.append(main(n))            # n is evaluated here at definition time

for f in func:
    f()

Output:

Now let’s implement the same logic to the function with lambda and see the unexpected behavior:

# Assigning the values
nums = 'one', 'two', 'three'

# Creating an empty list
func = []

# Using a for loop and applying "append" method to lambda
for n in nums:
    func.append(lambda: print(n))

for f in func:
    f()

Output:

The unexpected outcome happens because the lambda expression’s free variable x is bound at the time of execution. The lambda function is a closure that catches x, a runtime bound free variable. The value of x is three when running the function f() at runtime. Because of it it printed out threes in each answer.

To overcome this issues, we need to assign the free variables at definition time. See the example below:

# Assigning the values
nums = 'one', 'two', 'three'

# Creating an empty list
func = []

# Using a for loop and applying "append" method to lambda by defining variables
for n in nums:
    func.append(lambda n=n: print(n))

for f in func:
    f()

Output:

In terms of arguments, a Python lambda function is similar to a regular function. As a result, a default value can be assigned to a lambda parameter: the parameter n defaults to the outer n. The result would have been the same if the Python lambda function had been written as lambda x=n: print(x), so n would be used at definition time.

When is it better not to use lambda functions? #

When implementing lambda, we may run into issues in various circumstances. In such circumstances, the best alternative is to use a regular function. In the following section, we’ll look at certain situation where lambda should be avoided.

Raising an exception:

We should avoid using lambda if we wish to raise an exception. Instead, the best choice for addressing exceptions is to use a regular function. See the example of raised error and resolving the problem below:

# Using raise statement to get Exceprion
err = lambda: raise Exception()

print(err)

Output:

Now let’s rewrite this part of the code without using any statements:

def throw(ex): raise ex
(lambda: throw(Exception('for the tutorial')))()

Output:

In the above example, the workaround using the special function throw() is not syntactically correct, so it is better to avoid this workaround in favor of refactoring the code to use a normal function.

When should we use lambda functions? #

As you will see in the next section, there are several circumstances when lambda-function syntax is advantageous. Cases where lambda functions are not only appropriate but recommended in Python programming are shown in the following examples.

Lambda with DataFrames:

Lambda functions can be used to manipulate values within the Pandas DataFrames. See example in the code below: First, we’re creating our DataFrame.

# Importing pandas
import pandas as pd

# Creating DataFrame
df = pd.DataFrame({
    'Name': ['Scott','Elly','Samat','Emily'],
    'Status': ['Father', 'Mother', 'Son', 'Daughter'],
    'Birthyear': [1976, 1980, 2002, 2012],})

print(df)

Output:

Now let’s use lambda function to find the current age of each member by subtracting their birthyears from the current year.

# Applying lambda function to get the current age
df['Age'] = df['Birthyear'].apply(lambda x: 2021-x)

print(df)

Output:

Classic functional constructs: #

Lambda functions are frequently used with the map() and filter() built-in methods, as well as the functools.reduce() function from the functools module. The three examples below clearly represent how to use these functions with lambda expressions:

# Using map()
list(map(lambda x: x.upper(), ['a', 'b', 'c']))

# Using filter()
list(filter(lambda x: 'c' in x, ['a', 'b', 'c']))

# importing functools
from functools import reduce

# Using reduce
reduce(lambda acc, x: f'{acc} | {x}', ['a', 'b', 'c'])

Output:

Nevertheless, these constructs have equivalent alternatives that are considered more convenient to use.

Key functions:

Higher-order functions in Python that take the parameter key as a named argument are known as key functions. The key can receive lambda function. This function has a direct impact on the algorithm that is regulated by the key function. Here are some of key functions: sort( ), sorted( ), min( ), max( ), nlargest( ) and nsmallest( ).  Let’s see the example of using sorted( ) built-in function.

# Creating a list 
people = ['id1', 'id2', 'id30', 'id3', 'id22', 'id100']

print(sorted(people)) # Lexicographic sorting

sorted_people = sorted(people, key=lambda x: int(x[2:])) # Integer sorting

print(sorted_people

Output:

UI frameworks:

Lambda functions are used by UI frameworks like Tkinter, wxPython, etc. to map actions in response to UI events.

The following Tkinter program example demonstrates the use of a lambda for the Reverse button command:

# Importing tkinter 
import tkinter as tk
import sys    # Importing sys

# Creating window
window = tk.Tk()

# Creating one grid
window.grid_columnconfigure(0, weight=1)

# Name of our window
window.title("Lambda")

# Size of our window
window.geometry("300x100")

# Assigning our text to the label in the window
label = tk.Label(window, text="Lambda Function")
label.grid(column=0, row=0)

# Function & name of our button
button = tk.Button(
    window,
    text="Reverse",
    
    # Reverse function using lambda
    command=lambda: label.configure(text=label.cget("text")[::-1]),)

# Creating grid for our button
button.grid(column=0, row=1)

window.mainloop()

Output:

When you press the Reverse button, an event occurs that triggers a lambda function with the label Lambda Function changed to noitcnuF adbmaL.

Summary #

A lambda function in Python programming is an anonymous function or a function with no name. It is a small and restricted function having no more that one line. In this tutorial, we had covered lambda function, its implementation and advantages by solving many practical examples.

A complete copy of the source code can be found on GitHub How to work with lambda function in Python.ipynb

Powered by BetterDocs