How To Use Python Lambda Functions

Read it in 10 Mins

Last updated on
20th Jul, 2022
Published
29th Aug, 2019
Views
10,491
How To Use Python Lambda Functions

Modern-day programming languages like Python and other programming languages like C, C#, and Java have added lambda functions to their syntax to increase the functionality of the languages. 

Python Lambdas are anonymous functions which are small and restricted and come without an identifier.

Lambda Calculus is a universal model of computation which was formalized by Alonzo Church in the 1930s. In simple terms, it is a formal system in mathematical logic that is based on pure abstraction and can encode any Turing Machine.

Before proceeding further, check out this article on Self in Python!

The two models of computations— lambda calculus and Turing machines can be translated into one another. This equivalence is known as the Church-Turing Hypothesis. However, the lambda calculus is Turing complete and does not keep any state, unlike the Turing machine.  

The Turing machine has led to the discovery of the imperative style of programming. This type of approach requires managing states and comprises of programming with statements which drive the program in steps, using a comprehensive and detailed instruction format. Languages like C, Fortran, or Python uses this type of programming technique.

On the other hand, the lambda philosophy focuses on data abstraction, transformation, and purity. Their approach is declarative in nature and functional languages like Haskell, Lisp, or Erlang directly inherit them.

However, functional features are also a part of the imperative programming style, especially with the implementation of lambda functions in Python or Java.

Although Python is not a functional language, some functional features like map(), filter(), reduce(), and the lambda operator were added to Python in the year 1994. To learn more about such features, join our Python certification course.

What are Lambdas in Python?

Lambda expressions or lambda forms are used to create anonymous functions. Let us first see an identity function. It is an identity relation or identity map that returns the same argument that was originally provided as input. This is a standard Python definition defined using the def keyword:

>>> def identity(z):
...    return z

On the other hand, a Python lambda function can have any number of parameters and is written in a single line of code. However, the body of the function can have only one expression.

An example of a Python lambda construct:

>>> lambda z: z

The expression above comprises of three essential parts:

  • The lambda keyword.
  • The parameters.
  • The function body.

Another example of a lambda function with an argument:

>>> (lambda z: z + 1)(5)
6

Here, the argument and the function are surrounded by parentheses. The function adds 1 to the argument.

Since lambda function is an expression, you are allowed to name it whatever you want. Let us perform reduction to compute the value of an expression by naming the lambda function:

>>> add = lambda x: x + 1
>>> add(5)
6

We can also declare multi-argument function by listing arguments and separating them with commas ( , )  but without using parentheses:

>>> name = lambda first_name, last_name: f'The full name is {first.title()} {last.title()}'
>>> name('Alex', 'Pandian')
'The full name is Alex Pandian'

Here, the function add takes two arguments and returns a string by interpolating the two parameters first_name and last_name.

What are Anonymous Functions in Python?

A function without a name is known as an anonymous function in Python. It is created with the help of the keyword lambda.

An example of an anonymous function with two arguments:

>>> lambda a, b: a + b

Here, the function has two arguments x and y and is defined using lambda and is not bound to any variable. The function accepts the two arguments and returns their sum.

However, you can invoke the same function using an interactive interpreter-only feature of Python via the underscore(_):

>>> _(4, 5)
9

In the interactive interpreter, the single underscore(_) is bound to the last expression evaluated which means it points to the last lambda function.

The functions which take one or more arguments and returns the result are known as higher-order functions.

A lambda function can be a higher-order function:

>>> higher_order_function = lambda x, f1: x + f1(x)
>>> higher_order_function(3, lambda x: x * x)
12
>>> higher_order_function(3, lambda x: x + 3)
9

The higher-order functions are considered as built-in functions in the Python Standard Library. Some of the examples are filter(), map(), reduce() or key functions like min(), max(), sort() or sorted().

Python Lambdas vs Regular Functions

Python Lambdas vs Regular Functions| Differences between Lambda functions and regular functions in Python

A Lambda function might be considered a function with a syntax within a programming language which makes the language “sweeter” for human use. It is created to make things easier to read and express.

However, there are a number of precise differences between the regular functions and Python lambdas. The following section highlights them all.

A. Difference in Functions

In fundamental terms, there is almost no difference between a regular function with a single return line and a lambda function. However, let us check what difference it would make while we write it in code:

>>> import dis
>>> sub = lambda a, b: a + b
>>> type(sub)
<class 'function'>
>>> dis.dis(sub)
  1           0 LOAD_FAST                0 (x)
              2 LOAD_FAST                1 (y)
              4 BINARY_ADD
              6 RETURN_VALUE
>>> sub
<function <lambda> at 0x000001E47F3EC268>

The dis module subjects a readable version of the Python bytecode generated by the compiler, which in turn displays the low-level instructions used by the Python interpreter.

Let us try the same with a regular function:

>>> import dis
>>> def sub(x,y): return a + b
>>> type(sub)
<class 'function'>
>>> dis.dis(sub)
  1           0 LOAD_FAST                0 (x)
              2 LOAD_FAST                1 (y)
              4 BINARY_ADD
              6 RETURN_VALUE
>>> sub
<function <lambda> at 0x000001E47F3EC268>

You can see the Python bytecode is similar in both cases. However, what differs is the style of naming. With def, the function is named as sub whereas lambda is used in case of Python built-in function.

B. Difference in Traceback

An example of exception raising using lambda:

>>> divide_by_zero = lambda a: a / 0
>>> divide_by_zero(2)
Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
    File "<stdin>", line 1, in <lambda>
ZeroDivisionError: division by zero

When you’re using a lambda expression, the traceback of an exception identifies only the function, resulting in the exception as <lambda>.

On the other hand, when you’re using a regular function, the traceback provides the name of the function in particular:

>>> def divide_by_zero(a): return a / 0
>>> divide_by_zero(2)
Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
    File "<stdin>", line 1, in divide_by_zero
ZeroDivisionError: division by zero

The traceback returns the function name divide_by_zero.

C. Difference in Syntax

A lambda expression differs in syntax from a normal function in a number of ways:

  1.  Expressions and Statements

A lambda function can contain only expressions and cannot include any statements. It will raise a SyntaxError upon using statements like return, assert or raise.

Let us see an example of using an assert statement in a lambda function:

>>> (lambda a: assert a == )(2)
SyntaxError: invalid syntax

The interpreter raises an error while parsing the code since assert is not a valid keyword in a lambda expression.

  1.  Single Expression

In a Python lambda expression, you can distribute the expression across several lines using parentheses or a multi-line string. But the function will still remain as a single expression:

>>> (lambda a:
... (a % 2 and 'odd' or 'even'))(5)
'odd'

The lambda expression above spreads over two lines but still remains a single expression which evaluates a number and results into odd or even depending upon the argument.

  1.  Annotations

You can use Python type hints and type checking when you’re using normal functions:

def name(first_name: str, last_name: str) -> str:
    return f'{first_name.title()} {last_name.title()}'

If there are any errors with the function name, it can be caught using packages like mypy or pyre. On the other hand, if you’re using a lambda expression, a SyntaxError will be raised:

>>> lambda first_name: str, last_name: str: first_name.title() + " " + last_name.title() -> str
 File "<stdin>", line 1
    lambda first: str, last: str: first.title() + " " + last.title() -> str
SyntaxError: invalid syntax
  1.  IIFE

Immediately Invoked Function Execution or IIFE is a JavaScript function that gets executed as soon as it is defined. However, you cannot use it outside the Python Interpreter.  

An example of such is:

>>> (lambda a, b: a + b)(4,6)
10

The IIFE is mainly used to pass the definition of a Python function into a higher-order function like map(), filter() or reduce().

D. Difference in Arguments

Python supports different ways of passing arguments in a lambda expression just like regular functions. Some of which are as follows:

  • Named arguments.
  • Variable list of arguments.
  • Positional arguments.
  • Variable list of keyword arguments.
  • Keyword only arguments.

An example to illustrate different ways of passing arguments in a lambda function:

>>> (lambda a, b, c: a + b + c)(1, 2, 3)
6
>>> (lambda a, b, c=3: a + b + c)(1, 2)
6
>>> (lambda a, b, c=3: a + b +c)(1, b=2)
6
>>> (lambda *arg: sum(arg))(1,2,3)
6
>>> (lambda **kwarg: sum(kwarg.values()))(number_one=1, number_two=2, number_three=3)
6
>>> (lambda a, *, b=0, c=0: a + b + c)(1, b=2, c=3)
6

E. Difference in Decorators

A decorator is a function that accepts another function and allows adding a behavior to a function or class. Its syntax is @decorator which is written before a function.

An example of a decorator is:

def decorator(a):
    def wrap(*arguments):
        print(f"Calling function '{a.__name__}'")
        return a(arguments)
    return wrap
@decorator
def decorated_func(x):
    print(f"With argument '{x}'")

If we invoke decorated_func(“Hello World”), the output will be as follows:

Calling function 'decorated_func'
With argument 'Hello World'

The function decorated_func() prints With argument 'Hello World', but the decorator adds a behavior and also prints  Calling function 'decorated_func'

An example of applying decorator in lambda function is mentioned below:

# Defining a decorator
def trace(a):
  def wrap(*arg, **kwarg):
print(f"[TRACE] func: {f.__name__}, args: {arg}, kwarg: {kwarg}")
return a(*arg, **kwarg)
  return wrap
# Applying decorator to the function
@trace
def add(t):
    return t + 2
# Calling the decorated function
add(3)
# Applying decorator to a lambda function
print((trace(lambda y: y ** 2))(3))

If we execute the following code, we’ll get the following output:

[TRACE] func: add, arg: (3,), kwarg: {}
[TRACE] func: <lambda>, arg: (3,), kwarg: {}
9

The function add is decorated with @trace and invoked. The lambda function gets implemented to the decorator. The lambda function name is <lambda> and the regular function is add.

F. Difference in Closures

A closure is a function object that remembers values and is bound to a specified value in the enclosing scope of that function. In simple words, closure is a record that stores a function and defines the environment in which they run.

Both lambda functions and regular functions can be implemented as closures.

An example to show a closure construct with a regular function:

def outer_function(a):
    b = 4
    def inner_function(c):
        print(f"a = {a}, b = {b}, c = {c}")
        return a + b + c
    return inner_function
 
  for i in range(5):
    closure = outer_function(i)
    print(f"closure({i+5}) = {closure(i+5)}")

inner_function() is a nested function that is being returned by outer_function(). The nested Lambda function evaluates the sum of three arguments. The outer_function() is invoked 5 times in a for loop resulting in the output:  

x = 0, y = 4, z = 5
closure(5) = 9
x = 1, y = 4, z = 6
closure(6) = 11
x = 2, y = 4, z = 7
closure(7) = 13
x = 3, y = 4, z = 8
closure(5) = 15
x = 4, y = 4, z = 4
closure(5) = 017

An example to show closure using a Python lambda function:

def outer_function(a):
    b = 4
    return lambda c: a + b + c
 
for i in range(5):
    closure = outer_function(i)
    print(f"closure({i+5}) = {closure(i+5)}")

The output of the above code will be:

closure(5) = 9
closure(6) = 11
closure(7) = 13
closure(7) = 15
closure(7) = 17

In both situations, the normal function and the lambda function behaves precisely similar.

G. Difference in Evaluation Time

A lambda function’s behavior as a closure may be counter-intuitive, especially while working with loops.  

Using a regular function:

>>> def wrap(x):
...    def f():
...        print(x)
...    return f
...
>>> numbers = 'one', 'two', 'three'
>>> f1 = []
>>> for x in numbers:
...    f1.append(wrap(x))
...
>>> for f in f1:
...    f()
...
one
two
three

When the function f1 gets appended to the list, is evaluated at definition time.

Let us implement the same using lambda function:

>>> numbers = 'one', 'two', 'three'
>>> f1 = []
>>> for x in numbers:
...    f1.append(lambda: print(x))
...
>>> for f in f1:
...    f()
...
three
three
three

Since n is a free variable bound at runtime  when we invoke the function f, an unexpected error occurs printing three all the time.

However, to get rid of this situation, you can assign the free variable during definition time:

>>> numbers = 'one', 'two', 'three'
>>> f1 = []
>>> for x in numbers:
...    f1.append(lambda x=x: print(x))
... 
>>> for f in f1:
...    f()
...
one
two
three

The lambda argument is initialized with a default value. So, when the function is invoked, the default value x set at definition time is used.

Testing Lambdas

You can test Python lambdas similarly like regular functions with the help of two modules— unittest and doctest.

unittest

Testing Python lambda function using unittest:

import unittest
add = lambda x: x + 2
class Lambda_Testing(unittest.TestCase):
    def test_add1(self):
        self.assertEqual(add(2), 4)
    def test_add2(self):
        self.assertEqual(add(2.2), 4.2)
    def test_add3(self):
        # Should fail
        self.assertEqual(add(3), 6)
if __name__ == '__main__':
    unittest.main(verbosity=2)

The function Lambda_Testing tests three methods, each implemented as a lambda function. After execution, the Python file produces the following output:

$ python lambda_unittest.py
test_add3 (__main__.Lambda_Testing) ... FAIL
test_add1 (__main__.Lambda_Testing) ... ok
test_add2 (__main__.Lambda_Testing) ... ok
======================================================================
FAIL: test_add_three (__main__.Lambda.Testing)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "lambda_unittest.py", line 18, in test_add_three
    self.assertEqual(add3(3), 6)
AssertionError: 5 != 6
----------------------------------------------------------------------
Ran 3 tests in 0.001s
FAILED (failures=1)

There are two successful test cases and one failure for test_add3. However, if we change the expected result from 6 to 5, the Lambda_Testing will be satisfied for all tests.

doctest

The doctest module tests Python lambdas by extracting Python code from docstring:

add = lambda a: a + 2
add.__doc__ = """Add 2 to a number.
    >>> add(2)
    4
    >>> add(2.2)
    4.2
    >>> add(3) # Should fail
    6
    """

if __name__ == '__main__':
    import doctest
    doctest.testmod(verbose=True)

You can execute the code above using doctest.testmod(), the output will be:

$ python lambda_doctest.py
Trying:
    add(2)
Expecting:
    4
ok
Trying:
    add(2.2)
Expecting:
    4.2
ok
Trying:
    add(3) # Should fail
Expecting:
    6
**********************************************************************
File "lambda_doctest.py", line 16, in __main__.add
Failed example:
    add(3) # Should fail
Expected:
    6
Got:
    5
1 items had no tests:
    __main__
**********************************************************************
1 items had failures:
   1 of   3 in __main__.add
3 tests in 2 items.
2 passed and 1 failed.
***Test Failed*** 1 failures.

Here, the failure of the test result is also because of the same test failures explained above in the execution of test cases. Although  it is possible to use docstrings for lambda functions, Python recommends using docstrings for regular functions rather than lambda functions.

Where Should You Not Use Lambda Expressions?

You can simply say that if there is a situation where lambda expressions are not supported, normal functions would be more suitable in such places.

However, there might be situations when you need to avoid lambda expressions in your code:

  • When it doesn’t follow the Python Style Guide ( PEP 8 ).
  • When it is difficult to read and interpret.
  • When it doesn’t support readability.

Exception Raising 

An example of raising an exception in a Python lambda:

>>> def throw_except(excpt): raise excpt
>>> (lambda: throw_except(Exception('Exception is raised')))()
Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
    File "<stdin>", line 1, in <lambda>
    File "<stdin>", line 1, in throw
Exception: Something bad happened

Though there are some really clever ways of raising an exception in Python lambda, it is always suggested to use a normal function. This is because a statement is never correct in syntax in the body of a Python lambda function.

Cryptic Style

Lambda functions are brief in nature, because of which it might be difficult to read. 

An example of a bad writing style lambda  expression:

>>> (lambda _: list(map(lambda _: _ // 2, _)))([1,2,3,4,5,6,7,8,9,10])
[0, 1, 1, 2, 2, 3, 3, 4, 4, 5]

Let us modify this lambda code by naming the variables:

>>> (lambda some_list: list(map(lambda a: a // 2,
list)))([1,2,3,4,5,6,7,8,9,10])
[0, 1, 1, 2, 2, 3, 3, 4, 4, 5]

The code is still difficult to read and understand. However, if we execute the same code using a regular function by distributing the logic across several lines, it would greatly impact the readability of the code:

>>> def divide(list):
      divide = lambda a: a // 2
      return map(divide, list)
>>> list(divide([1,2,3,4,5,6,7,8,9,10])))
[0, 1, 1, 2, 2, 3, 3, 4, 4, 5]

Though this method is not the best, it surely enhances the readability of the code in comparison to the lambda expressions.

Python Class

An example of a Python class using lambda:

class Car:
    """Car with methods as lambda functions."""
    def __init__(s, brand, year):
        s.brand = brand
        s.year = year
    brand = property(lambda s: getattr(s, '_brand'),
                    lambda s, value: setattr(s, '_brand', value))
    year = property(lambda s: getattr(s, '_year'),
                    lambda s, value: setattr(s, '_year', value))
    __str__ = lambda s: f'{s.brand} {s.year}'  # 1: error E731
    honk = lambda s: print('Honk!')     # 2: error E731

If you use a style guide tool like flake8 for checking the code above, it will result in following errors for __str__ and honk:

E731 do not assign a lambda expression, use a def

Since multiple strings like '_brand and'_year' are used, the properties that are difficult to read are inclined to errors.

Ultimately, considering a general rule, you should have a preference for regular functions instead of lambda functions in the context of Python programming.  

Where should you use lambda expressions?

Lambdas provide a shortened syntax for writing functions, which return a single expression. Since Python supports functional programming, lambdas are one of the most common use cases in such a paradigm of programming. Lambdas allow a fine way to pass functions as arguments to another function by creating a one-time function and passing it as a parameter.

Let us discuss some situations where using lambdas are very appropriate and also suggested by Python.

Functional Constructs 

Built-in functions like map(), filter(), and functools.reduce(), found in the functools module, are often used along with lambda expressions.  

Three examples of those functions along with lambda expressions:

>>> list(map(lambda x: x.upper(), ['alex', 'bob', 'charles']))
['ALEX', 'BOB', 'CHARLES']
>>> list(filter(lambda x: 'o' in x, ['alex', 'bob', 'charles']))
['BOB']
>>> from functools import reduce
>>> reduce(lambda acc, x: f'{acc} | {x}', ['alex', 'bob', 'charles'])
'alex | bob | charles'

There are other functional constructs which are alternatives to these constructs and are considered more Pythonic in nature.

Key Functions

Python key functions are higher-order functions that directly determine the algorithm driven by the key function. They accept a named parameter known as key, which in turn takes a lambda function.

Some of the most commonly used key functions are sort(), sorted, min(), max(), nlargest() and nsmallest().

Let us sort a list of IDs using sorted(), in which each ID is a concatenated string of string id and a number. To sort the list of IDs with the help of the corresponding number associated, we’ll assign lambda to the key argument:  

>>> ids = ['ID1', 'ID9', 'ID11', 'ID3', 'ID25', 'ID200']
>>> print(sorted(ids))#Sorting
['ID1', 'ID9', 'ID11', 'ID3', 'ID25', 'ID200']
>>> sorted_ids = sorted(ids, key=lambda x: int(x[2:]))#Integer sorting
>>> print(sorted_ids)
['ID1', 'ID9', 'ID11', 'ID3', 'ID25', 'ID200']

UI Frameworks

Some of the UI frameworks that use lambda expressions for their UI events are Tkinter, wxPython or .NET Windows Forms.

An example of a Tkinter program using lambda:

import tkinter as t
import sys
window = t.Tk()
window.grid_columnconfigure(0, weight=1)
window.title("Lambda Calculus")
window.geometry("500x200")
label = t.Label(window, text="HELLO WORLD")
label.grid(column=0, row=0)
button = t.Button(
    window,
    text="Reverse the string",
    command=lambda: label.configure(text=label.cget("text")[::-1]),
)
button.grid(column=0, row=1)
window.mainloop()

When the Reverse button is clicked, an event gets fired that triggers the lambda, which in turn changes the label from HELLO WORLD  to DLROW OLLEH.

Though lambda can handle firing events, it is always recommended to use a normal function instead as the lambda ends up being self-contained in a shortcode structure.

The Python Interpreter

Python lambdas are considered as bliss when you use it inside the Python Interpreter. In comparison to regular functions, creating a single-line lambda function to look into some code is much easier. The lambdas in interpreter are just like scrap paper that is useless after being used once and are later thrown away.

The timeit module

timeit is a Python module that is used to time small fragments of Python code. In simple words, it measures the evaluation time of small code snippets.

An example of timeit.timeit() being called directly:

>>> from timeit import timeit
>>> timeit("factorial(100)", "from math import factorial", number = 5)
1.799199999652501e-05

A NameError exception would be raised if the full context of the statement is not passed as a string. So the environment should be set up that is required by the main function to be timed.

However a  cleaner and more readable solution to the above code is by using a lambda function:

>>> from math import factorial
>>> timeit(lambda: factorial(100), number=5)
1.799199999652501e-05

Although the lambda version takes less execution time, the string version has a benefit of executing the functions again.

What are the Alternatives of Lambda Expressions?

The higher-order functions like map(), filter(), and functools.reduce() can be used in place of lambdas by changing the form with lists and generators.

Using map() 

map() takes two arguments— a function and an iterable like string, lists or tuples. It executes the function object for each element and returns a modified list of elements.

An example to illustrate map() with lambda by transforming a string of integers into its capitalized form:

>>> list(map(lambda x: x.capitalize(), ['alex', 'bob', 'charles']))
['Alex', 'Bob', 'Charles']

list() should be invoked so that the iterator gets converted into the modified list which can be shown as output in the Python Interpreter.

Using filter()

filter() accepts two arguments— a boolean-valued function and an iterable. It is an excellent way to filter out all elements of a sequence for only those which returns True.

An example of filter() with lambda which returns all odd numbers from a list:

my_list = [5, 10, 21, 95, 51, 69, 72, 22, 70, 99]
final_list = list(filter(lambda x: (x%2 != 0) , my_list))
print(final_list)
[5, 21, 95, 51, 69, 99]

The filter() function returns the list of elements, just like map().

Using reduce()

reduce() is a functools module function that takes two arguments just like map() and filter(). However, it might also take an initializer as a third argument as the initial value of the accumulator.

An example of reduce() with lambda to calculate the sum of a list:

from functools import reduce
my_list = [5, 8, 12, 25, 50, 500]
sum = reduce((lambda x, y: x + y), my_list)
print(sum)
600

The Pythonic Nature of Lambda Expressions

The style guide of Python, PEP 8 discourages lambdas and recommends using normal functions since it is more beneficial. On the other hand, though lambda functions have some good uses in Python, they have a lot of limitations.

Lambdas are meant to be used as anonymous functions that will be used especially with functions like map(), filter(), and reduce(). So, you can consider them to be Pythonic since they are quick and elegant in their use and they depend on how you use them.

In simple terms, lambdas are considered Pythonic if there are no Pythonic elements available other than that.

Summary

Let us sum up what we’ve learned in this article so far:

  • Lambdas and Anonymous Functions.
  • Where should we use a lambda.
  • When should we use a  regular function.
  • Lambda expression abuses.
  • Appropriate uses of Lambdas.
  • Lambda alternatives.
  • Pythonic nature of Lambda.

Python lambdas are considered as salt. If used in small amounts, it will enhance the flavor but if used more, the dish will be spoiled. If you are intending to know more about Lambda and Lambda Calculus, you can refer to the following links—

To gain more knowledge about Python tips and tricks, check our Python tutorial and get a good hold over coding in Python by joining the Python certification course.

Profile

Priyankur Sarkar

Data Science Enthusiast

Priyankur Sarkar loves to play with data and get insightful results out of it, then turn those data insights and results in business growth. He is an electronics engineer with a versatile experience as an individual contributor and leading teams, and has actively worked towards building Machine Learning capabilities for organizations.