Python declares anonymous functions as ‘ lambda ‘ and is therefore called ‘ lambda functions. ‘ Essentially, an anonymous function is a function that is defined by o name. Such functions are necessary only where they are generated and are therefore often referred to as throwaway functions.
A lambda function can only have one expression, although it can take several arguments. An expression is a small piece of code entered into a lambda function as an argument that may or may not return a value. A lambda function is often passed to another higher-order function as an argument. Lambda functions are also used along with other built-in functions like functools.reduce(), filter(), and map().
Syntax of Lambda Functions
The common syntax of a lambda function is:
lambda argument_list: expression
1. Lambda is the keyword which we use to define a lambda function.
2. The argument list is the list of arguments separated by a comma.
3. The expression is an arithmetic statement written using these arguments.
Download Detailed Brochure and Get Complimentary access to Live Online Demo Class with Industry Expert.
An example of a lambda function is given as follows:
>>> f = lambda a, b : a + b >>> f(1,1) 2
Here, we have assigned the function to a variable in order to give it a name. Lambda functions are used when function objects are required. Here is another example of a lambda function used to multiply a number with 2:
double = lambda a: a * 2 # Output: 10 print(double(5))
As we can observe, lambda a: a * 2 is the lambda function, with ‘a’ as an argument and a * 2 as an expression. Here, we can see that the function has no name and it returns an object which is assigned to the variable double.
Python Lambda & Regular Functions:
A lambda form is syntactically different from a normal function. Lambda function has the following characteristics:
1. It cannot include statements in its body.
2. It can only contain expressions.
3. It is written as a single line of execution.
4. It does not support type annotations.
5. It can be immediately invoked (IIFE).
Let’s discuss these characteristics in details:
It cannot include statements in its body
In a lambda function, we cannot use statements like return, pass, raise, or assert since these will raise a “SyntaxError” exception. We can refer to the following example below:
>>> (lambda a: assert a == 2)(2) File "<input>", line 1 (lambda a: assert a == 2)(2) ^
SyntaxError: invalid syntax
Here, in this piece of code, it is evident that the “assert” statement throws a syntax error.
It is written as a single expression
A Python lambda function is a single expression, unlike normal functions. In the body of a lambda, you can continue the expression over multiple lines using parentheses or a multiline string, but it remains a single expression. We can illustrate this point through the following example:
>>> (lambda n: ... (n % 2 and 'odd' or 'even'))(3) 'Odd'
In this code, the lambda function returns “odd”, when the argument passed is odd, and returns “even” when an argument is an even number. The function is written in two lines, still, it is considered as a single expression only.
Type Annotation
There is no equivalent for the following in the lambda function:
def full_name(fname: str, lname: str) -> str: return f'{fname.title()} {lname.title()}'
Any type error with full_name() can be detected by tools like mypy or pyre, whereas a SyntaxError with the equivalent lambda function is thrown at runtime:
>>> lambda fname: str, last: str: fname.title() + " " + lname.title() -> str File "<stdin>", line 1 lambda fname: str, last: str: fname.title() + " " + lname.title() -> str SyntaxError: invalid syntax
Immediately Invoked Function Execution
This feature is mostly not used outside the Python interpreter. Since the lambda function can be called as it is defined directly as an argument, hence it being an IIFE is a consequence of the same. This feature allows you to pass the definition of a Python lambda expression to a higher-order functions like map(), filter(), or functools.reduce(), or to a key function.
Arguments in a Lambda Expression
Python lambda expression supports all the ways of passing an argument like any other normal function defined objects. The different ways to pass an argument include:
1. Positional arguments
2. Keyword-only arguments
3. Variable list of arguments (often referred to as varargs)
4. Named arguments (sometimes called keyword arguments)
5. Variable list of keyword arguments.
Following are the different ways of passing an argument in a lambda expression:
>>> (lambda x, y, z: x + y + z)(1, 2, 3) 6 >>> (lambda x, y, z=3: x + y + z)(1, 2) 6 >>> (lambda x, y, z=3: x + y + z)(1, y=2) 6 >>> (lambda *args: sum(args))(1,2,3) 6 >>> (lambda **kwargs: sum(kwargs.values()))(one=1, two=2, three=3) 6 >>> (lambda x, *, y=0, z=0: x + y + z)(1, y=2, z=3) 6
Decorators
A decorator is a function that takes another function as an argument and extends its functionality without changing it. It provides a simple syntax for calling higher order functions. Hence it allows the addition of a behavior to a function or a class. Here, is an example of a decorator:
def some_decorator(f): def wraps(*args): print(f"Calling function '{f.__name__}'") return f(args) return wraps @some_decorator def decorated_function(x): print(f"With argument '{x}'")
It returns the following output.
Calling function ‘decorated_function’
With argument ‘Python’
Hence we observe that decorated_function() only prints With argument ‘Python’, but the decorator adds an extra behavior that also prints Calling function ‘decorated_function’.
A decorator can be applied to a lambda function as well although it is not possible to decorate it with @decorator syntax. But since, decorator is also a function it can call lambda function. Here is an example:
# Defining a decorator 2 def trace(f): 3 def wrap(*args, **kwargs): 4 print(f"[TRACE] func: {f.__name__}, args: {args}, kwargs: {kwargs}") 5 return f(*args, **kwargs) 6 7 return wrap 8 9 # Applying decorator to a function 10 @trace 11 def add_two(x): 12 return x + 2 13 14 # Calling the decorated function 15 add_two(3) 16 17 # Applying decorator to a lambda 18 print((trace(lambda x: x ** 2))(3)) On running the above code, we get the following output: [TRACE] func: add_two, args: (3,), kwargs: {} [TRACE] func: <lambda>, args: (3,), kwargs: {} 9
This shows that the name of the lambda function appears as <lambda>, whereas add_two is clearly identified for the normal function. This can be useful for debugging purposes, usually to debug the behavior of a lambda function when it is passed an argument to higher order functions.
In the following section, we discuss the use of lambda function as along with built-in functions.
Filter() function
The function filter(function, list) is used to filter out all the elements of a list, for which the function returns True. The function filter (function, list) needs a function ‘function’ as its first argument where it returns a Boolean value, i.e. either True or False. This function will be applied to every element of the list ‘list’. Only if the function, here named “function” returns True will the element of the list be included in the result list. Since lambda can be passed as an argument, here is an example of the same:
# Program to filter out only the even items from a list
- my_list = [1, 5, 4, 6, 8, 11, 3, 12]
- new_list = list(filter(lambda x: (x%2 == 0) , my_list))
- # Output: [4, 6, 8, 12]
- print(new_list)
Map() function
map() function is a built-in function which takes two arguments. The syntax of a map() function is:
r = map(function, seq)
The first argument function is the name of a function and the second argument is a sequence (e.g. a list) seq. map() applies the function “function” to all the elements of the sequence seq. It returns a new list with the elements changed by “function”. Here is an example of the map() function:
def fahrenheit(T): return ((float(9)/5)*T + 32) def celsius(T): return (float(5)/9)*(T-32) temp = (36.5, 37, 37.5,39) F = map(fahrenheit, temp) C = map(celsius, F) Here is the same code using lambda function. >>> Celsius = [39.2, 36.5, 37.3, 37.8] >>> Fahrenheit = map(lambda x: (float(9)/5)*x + 32, Celsius) >>> print Fahrenheit [102.56, 97.700000000000003, 99.140000000000001, 100.03999999999999] >>> C = map(lambda x: (float(5)/9)*(x-32), Fahrenheit) >>> print C [39.200000000000003, 36.5, 37.300000000000004, 37.799999999999997] >>>
We notice that since lambda is an anonymous function, we need not define the names of the functions.
reduce() function
Like map() and filter(), the first two arguments of reduce() function are respectively a function and an iterable. It may also take a third argument that is an initializer which is used as the initial value of the resulting accumulator. For each element of the iterable, reduce() applies the function and accumulates the result that is returned when the iterable is exhausted. The following code can be written to apply reduce() to a list of pairs and calculate the sum of the first item of each pair:
>>> import functools >>> pairs = [(1, 'a'), (2, 'b'), (3, 'c')] >>> functools.reduce(lambda acc, pair: acc + pair[0], pairs, 0) 6 Here is an another example of using lambda with reduce() function: >>> f = lambda a,b: a if (a > b) else b >>> reduce(f, [47,11,42,102,13]) 102
It returns the maximum of all values in the list.
Key functions
Key functions are higher-order functions in Python which has a default named argument “key” that takes a function. We can use a lambda function as a key argument. Some of the key functions are as follows:
sort() sorted(), min(), max() nlargest() and nsmallest() Following is an example of the use of lambda function in the key functions. >>> ids = ['id1', 'id2', 'id30', 'id3', 'id22', 'id100'] >>> print(sorted(ids)) # Lexicographic sort ['id1', 'id2', 'id30', 'id3', 'id22', 'id100'] >>> sorted_ids = sorted(ids, key=lambda x: int(x[2:])) # Integer sort >>> print(sorted_ids) ['id1', 'id2', 'id3', 'id22', 'id30', 'id100']
Here are some examples where the use of lambda functions is encouraged:
Classic Functional Constructs
Lambda functions are often used with built-in functions like filter(), map(), and functools.reduce(). Take a look at the following example with illustrations of lambda expressions with the above-mentioned functions:
>>> >>> list(map(lambda x: x.upper(), ['cat', 'dog', 'cow'])) ['CAT', 'DOG', 'COW'] >>> list(filter(lambda x: 'o' in x, ['cat', 'dog', 'cow'])) ['dog', 'cow'] >>> from functools import reduce >>> reduce(lambda acc, x: f'{acc} | {x}', ['cat', 'dog', 'cow']) 'cat | dog | cow'
Key Functions
These are the higher-order functions of python that take key as a named argument. It receives the lambda function which influences the algorithm. Here are some of the key functions:
sorted(), min(), max(): built-in functions
sort(): list method
nlargest() and nsmallest(): in heapq, the Heap queue algorithm module
Suppose that you have to sort some IDs that are represented as strings. Every ID is made of a number and a string id. If you use sorted(), a lexicographic order will be used for sorting the elements of the list. You can assign lambda to the key argument for influencing the sorting execution so that the number in the ID is used for sorting:
>>> >>> ids = ['id1', 'id2', 'id30', 'id3', 'id22', 'id100'] >>> print(sorted(ids)) # Lexicographic sort ['id1', 'id2', 'id30', 'id3', 'id22', 'id100'] >>> sorted_ids = sorted(ids, key=lambda x: int(x[2:])) # Integer sort >>> print(sorted_ids) ['id1', 'id2', 'id3', 'id22', 'id30', 'id100']
UI Frameworks
To map functions responding to the UI events, lambda functions are highly used in UI frameworks like wxPython, Tkinter, or .NET Window Forms. Here is a native program of Tkinter demonstrating the use of lambda:
import tkinter as tk import sys window = tk.Tk() window.grid_columnconfigure(0, weight=1) window.title("Lambda") window.geometry("300x100") label = tk.Label(window, text="Lambda Calculus") label.grid(column=0, row=0) button = tk.Button( window, text="Reverse", command=lambda: label.configure(text=label.cget("text")[::-1]), ) button.grid(column=0, row=1) window.mainloop()
As you click the button Reverse, an event will be fired that will trigger the lambda function that changes the label to suluclaC adbmaL* from Lamda Calculus.
A similar approach for handling events is used by IronPython and wxPython on the .NET platform.
Python Interpreter
Python Lambda functions can help while playing with code in the interpreter. You can easily create a one-liner function for exploring snippets of code. Consider the lambda as a scrap paper used for speedy delivery that can be thrown away easily.
timeit
The timeit module is used for experimenting in the python interpreter with its functions used for timing small code fragments. You can directly call timeit.timeit() by passing python code in the string. Take a look at this example:
>>> >>> from timeit import timeit >>> timeit("factorial(999)", "from math import factorial", number=10) 0.0013087529951008037
When you pass the whole statement as a string, the full context is required by the timeit(). As you can see in the above example, the second argument provides this context and sets up the environment the main function needs to be times. Failure to do so will raise a NameError exception.
Here is a different way in which you can use a lambda function:
>>> >>> from math import factorial >>> timeit(lambda: factorial(999), number=10) 0.0012704220062005334
As you can see, this solution is more readable, cleaner and can be quickly typed in the interpreter. The lambda version has a slightly less execution time. However, if you execute the functions again, the string version will have a slight advantage. The overall execution time does not include the setup’s execution time and thus has no impact on the result.
Monkey Patching
Sometimes, you have to rely on repeatable results during the given software’s normal execution for testing purposes. This is to determine whether the results differ a little or are totally random. For example, you need to test a function that is responsible for handling random values at runtime. For the testing execution, you need to start asserting unpredictable values repeatedly. Here is an example where you have to use a lambda function for monkey patching.
from contextlib import contextmanager
import secrets
def gen_token(): """Generate a random token.""" return f'TOKEN_{secrets.token_hex(8)}' @contextmanager def mock_token(): """Context manager to monkey patch the secrets.token_hex function during testing. """ default_token_hex = secrets.token_hex secrets.token_hex = lambda _: 'feedfacecafebeef' yield secrets.token_hex = default_token_hex def test_gen_key(): """Test the random token.""" with mock_token(): assert gen_token() == f"TOKEN_{'feedfacecafebeef'}" test_gen_key()
To insulate the monkey patching’s operation of a function from the standard library like secrets, you need the help of a context manager. The secrets.token_hex() is assigned a lambda function for substituting the default variable after returning a static value.
Doing this will allow you to test a function that depends on token_hex() in a predictable manner. Before exiting the context manager, the token_hex()’s default behavior is reestablished for eliminating any unexpected side effects that can affect other areas involved in testing that are dependent on the token_hex()’s default behavior.
For making this concept more sophisticated, unit test frameworks like pytest and unittest are used. The example mentioned above becomes more concise and elegant after the lambda function is used with pytes:
import secrets
def gen_token(): return f'TOKEN_{secrets.token_hex(8)}' def test_gen_key(monkeypatch): monkeypatch.setattr('secrets.token_hex', lambda _: 'feedfacecafebeef') assert gen_token() == f"TOKEN_{'feedfacecafebeef'}"
With the help of monkeypatch fixture of pytest, the lambda overwrites the secrets.token_hex() that will now return feedfacecafebeef, a deterministic value that allows the validation of the test. With this fixture, you will also have the control of scoping the override. As you can see in the above example, when you don’t use monkey patching to invoke secrets.token_hex() in the tests, the normal implementation of the function will be executed. Here is an example of execution of pytest test:
$ pytest test_token.py -v
============================= test session starts ==============================
platform linux -- Python 3.7.2, pytest-4.3.0, py-1.8.0, pluggy-0.9.0 cachedir: .pytest_cache rootdir: /home/andre/AB/tools/bpython, inifile: collected 1 item
test_token.py::test_gen_key PASSED [100%]
=========================== 1 passed in 0.01 seconds ===========================
When you validate that gen_token() was exercised, the test passes.
Now you know how to write lambdas functions of python and use anonymous functions. You need to remember that you have to avoid using lambdas excessively and where you should use lambdas and where normal functions of python.
If you also want to become a Python Programmer, enroll yourself in a Python Programming Course and enrol the coding world like a pro.