Introduction to Python lambda
In Python and other programming languages, Lambda expressions have their roots in lambda calculus. Which was invented by Alozno Church. Lambda in Python are little anonymous functions, subject to a more restrictive but more concise syntax than regular Python functions. In this tutorial we will learn about python lambda functions and how they are related to regular functions. Moreover, we will also cover how to write lambda functions and how to use it properly.
Python lambda as Anonymous function
- The
lambda
keyword creates an anonymous function within a Python expression. - However, the simple syntax of Python limits the body of lambda functions to be pure expressions.
- In other words, the body cannot contain other Python statements such as
while
,try
, etc. Assignment with=
is also a statement, so it cannot occur in a lambda. - The new assignment expression syntax using
:=
can be used, but if you need it, your lambda is probably too complicated and hard to read, and it should be refactored into a regular function usingdef
.
Problems with Anonymous Functions
- Beyond the Python-specific syntax constraints, anonymous functions have a serious drawback in any language.
- Stack traces are easier to read when functions have names.
- Anonymous functions are a handy shortcut, people have fun coding with them, but sometimes they get carried away—especially if the language and environment encourage deep nesting of anonymous functions, like JavaScript on Node.js do.
- Lots of nested anonymous functions make debugging and error handling hard.
- Asynchronous programming in Python is more structured, perhaps because the limited lambda syntax prevents its abuse and forces a more explicit approach.
Syntax of Python lambda function
A lambda expression creates a function object just like the def
statement. Here is the syntax used to define a function using lambda:
lambda argument: argument
Notice that we didn’t define a name to function , we just used keyword lambda
and passed argument.
As already informed python lambda function only contains expressions and does not contain statements in the body. Statements like return
, pass
, assert
, or raise
will raise a syntaxError
exception.
See the example below which raises a syntax error when we try to use return
in lambda.
In contrast to a normal function, a Python lambda function is a single expression. Although, in the body of a lambda, you can spread the expression over several lines using parentheses or a multi-line string, it remains a single expression. See example below.
#lambda funtion in more than one line
#check even and odd number
number = (lambda x:
(x % 2 and 'odd' or 'even'))(3)
#printing
print(number)
Output:
odd
The example above returns odd when the argument is odd and returns even when we provide an even number as an argument. Although the code contains multiple lines, it still is counted as a single expression.
How to use Python lambda function
Here we will first take some examples of python functions and then will convert those into lambda functions. Let us start working with lambda by taking identity functions. Identity functions are those functions that return their arguments.
A very simple identity function is Python will be:
def main(argument):
return argument
This function returns its argument whenever you call it. Now let us try to convert the same function in Lambda. See the python function which returns the square of the number.
def main(argument):
return argument*argument
If we try to write the same function in lambda, it will be something like this.
lambda argument: argument*argument
Moreover, we can also store the return value of lambda function in a variable and later use that name to get access to the return value. See the example below which similar to the above example.
square = lambda argument: argument*argument
As in the definition we said that lambda can take multiple arguments. Let us now take an example to see how multiple arguments in lambda works.
# defining lambda function with multiple arguments
Multiply = lambda num1, num2: num1*num2
# printing the function
print(Multiply(2, 3))
Output:
6
Notice that multiple arguments in lambda are not inside any parentheses, they are just separated by commas.
We can pass arguments to the lambda function, immediately after defining it in the same line. See example below;
# defining lambda function and passing arguments
square = (lambda num1, num2: num1*num2)(2, 4)
#printing the function
print(square)
Output:
8
Compare Python lambda vs Python functions
You might wonder what is the main thing that differentiate between lambda and other python functions. Now in this section let us see how Python sees a function with a return single return statement and a lambda function.
First we have to import
a python module named dis
which exposes functions to analyse Python bytecode generated by the Python compiler.
import dis
# defining function
def main(argument):
# Return argument
return argument*argument
#printing the type
print(type(main))
#printing name
print(main)
Output:
<class 'function'>
<function main at 0x7f3ab5f7c1f0>
Now we define the same logic using lambda
function:
# Importing dis module
import dis
# defining lambda function and passing arguments
square = lambda num1, num2: num1*num2
# printing the typeof square defined above
print(type(square))
# printing name
print(square)
Output:
<class 'function'>
<function <lambda> at 0x7f8b5005d1f0>
The bytecode interpreted by Python is the same for both functions. But you may notice that the naming is different: the function name is main for a function defined with def
, whereas the Python lambda function is seen as lambda
.
Passing arguments in python lambda
Like a normal function, lambda allows us to pass arguments in many ways. We can pass positional arguments, named arguments, variable lists, and keyword arguments as well.
Example-1: Pass arguments to lambda function
See the following examples to get familiar with different ways to pass arguments.
#passing arguments in lambda
number = (lambda a, b: a + b)(1, 3)
#printing
print(number)
Output:
4
Example-2: Define variables inside lambda function
we can also define variables inside the lambda. See the example below where we defined variable b and equalize it to 3.
#passing arguments in lambda
number = (lambda a, b=3: a + b)(1)
#printing
print(number)
Output:
4
Example-3: Perform operation on the arguments
lambda also allows us to take as many arguments and return a function. See the example below where sum()
function is returned.
#passing arguments in lambda
number = (lambda *args: sum(args))(2,2,2)
#printing
print(number)
Output:
6
Example-4: Handle return in lambda function
Moreover, we can take defined variable values as argument and return through a function as a return statement. See the example below.
#passing arguments in lambda
number = (lambda **kwargs: sum(kwargs.values()))(a=2, b=2, c=3)
#printing
print(number)
Output:
7
Closure function in lambda
A closure
function is a nested function which has access to a free variable from an encoding function that has finished its execution. There are mainly three main characteristics of closure function in Python.
- First, it is a nested function.
- Second, it has access to a free variable (a variable that is not bound in the local scope) in outer space
- Tthird characteristic is it is returned from the enclosing function.
The concepts of lambdas and closures are not necessarily related, although lambda functions can be closures in the same way that normal functions can also be closures. Here is an example of a closure
function in python.
# This is the outer enclosing function
def message(msg):
# This is the nested function
def printer():
#printing
print(msg)
return printer # returns the nested function
# Now let's try calling this function.
another_message = message("Hello")
another_message()
Output:
Hello
In a similar way lambda can also be used as a closure
function. See the example below:
## This is the outer enclosing function
def message():
# This is the nested function
return lambda : "Hello"
closure = message()
print(closure())
Output:
Hello
Evaluation time of lambda
Sometimes using lambda in a loop can behave unexpectedly. The following examples demonstrate the difference when using a regular function vs using a Python lambda. Let first take a regular function as an example.
def main(x):
# Nested function
def f():
print(x)
return f
num = 'first', 'second', 'third'
# creating empty list
fun = []
for x in num:
fun.append(main(x))
for f in fun:
# calling nested function
f()
Output:
first
second
third
In a normal function, x
is evaluated at definition time, when the function is added to the list: fun.append(main(x))
.
Now, let us implement the same logic in lambda
.
num = 'first', 'second', 'third'
# creating empty list
fun = []
# for loop
for x in num:
fun.append(lambda: print(x))
# calling the f() using loop
for f in fun:
f()
Output:
third
third
third
The unexpected result occurs because the free variable x
, as implemented, is bound at the execution time of the lambda expression. The Python lambda function is a closure that captures x
, a free variable bound at runtime. At runtime, while invoking the function f()
the value of x
is three.
We can overcome this issue by free
variable at definition time. See the example below.
num = 'first', 'second', 'third'
# creating empty list
fun = []
# for loop
for x in num:
fun.append(lambda x=x: print(x))
# calling the f() using loop
for f in fun:
f()
Output:
first
second
third
When to avoid using Python lambda function
In some cases we might face problems while using lambda. So, in such cases using a regular function would be the best option. In the following section, we will see some examples where the use of lambda should be avoided.
Raising an exception in lambda
If we want to raise an exception, we should avoid using it with lambda. Rather using a regular function for exception handling will be the best option. See the following example, which gives you an error if you want to raise an exception with lambda.
Python class and lambda
We can use lambda to write methods in Python but we should avoid using it. See the example below, which is legal Python code but exhibits unconventional python code relying on lambda. For example, instead of implementing __str__
as a regular function, it uses a lambda. Similarly, brand and year are properties also implemented with lambda functions, instead of regular functions or decorators:
# Python class
class Person:
"""Person information using lambda."""
def init__(self, name, age):
self.name = name
self.age = age
# using lambda instead of def
name = property(lambda self: getattr(self, '_name'),
lambda self, value: setattr(self, '_name', value))
# Using lambda instead of def
age = property(lambda self: getattr(self, '_age'),
lambda self, value: setattr(self, '_age', value))
str__ = lambda self: f'{self.name} {self.age}'
BA = lambda self: print('BA!')
This code gives error error E731
. We should avoid using lambda in python class and instead should use def
.
Usage of Python Lambda
In the functional programming paradigm, some of the best known higher-order functions are map
, filter
, reduce
, and apply
.
The apply
function was deprecated in Python 2.3 and removed in Python 3 because it’s no longer necessary. If you need to call a function with a dynamic set of arguments, you can write fn(*args, **kwargs)
instead of apply(fn, args, kwargs)
.
The map
, filter
, and reduce
higher-order functions are still around, but better alternatives are available for most of their use cases, as the next section shows.
Replacement of filter() function
The filter()
is a python built-in function that takes a predicate as a first argument and an iterable as a second argument. It builds an iterator containing all the elements of the initial collection that satisfies the predicate function.
Here we try to find a list of factorials of odd numbers up to 5!, using both map
and filter
.
>>> list(map(factorial, filter(lambda n: n % 2, range(6))))
[1, 6, 120]
List comprehension does the same job, replacing map and filter, and making lambda unnecessary.
>>> [factorial(n) for n in range(6) if n % 2]
[1, 6, 120]
>>>
Let us see one more example:
# creating lambda
ODD= lambda x: x%2 != 0
# filtering and storing in list
odd_list = list(filter(ODD, range(11)))
# printing the list
print(odd_list)
Output:
[1, 3, 5, 7, 9]
The same logic can be implemented in another way without using lambda. See the example below.
#printing the list of odd numbers
print([x for x in range(11) if x%2 != 0])
Output:
[1, 3, 5, 7, 9]
Replacement of map() function
The map()
is a python built-in function that takes a function as the first argument and applies it to each of the elements of its second argument, an iterable. Examples of iterables include strings, lists and tuples. The map()
returns an iterator corresponding to the transformed collection. For example, we can use map()
to transform a list of strings to a new list with each string capitalized.
# list of names
names = ['bashir', 'alam', 'arylen']
# printing list and capitalizing using lambda
print(list(map(lambda x: x.capitalize(), names)))
Output:
['Bashir', 'Alam', 'Arylen']
Now let's see how we can achieve the same logic without using lambda.
# list of names
names = ['bashir', 'alam', 'arylen']
# printing list and capitalizing without using lambda
print([x.capitalize() for x in names])
Output:
['Bashir', 'Alam', 'Arylen']
Summary
Lambda is also known as an anonymous function because of its anonymous behavior. It can take any number of arguments but only one expression. The expression is evaluated and returned. Lambda functions can be used wherever function objects are required. In this tutorial we learned about lambda, their anonymous behavior and problems that lambda can cause in some cases. Moreover, we also learned about some cases where we should avoid using lambda.