Python offers a huge variety of features, inbuilt libraries and functions. One of these functions or features offered by python is the 'lambda' function. In this article we will study about this lambda function, how can it be used, where can it be used and a lot more.
Although the name 'lambda' can conjure up images of difficulty, the truth is quite the reverse. How? keep reading further to figure this out.
Lambda calculus, a form of computing developed by Alonzo Church, serves as the foundation for lambda expressions in Python.
In the 1930s, Alonzo Church formalised lambda calculus, a pure abstraction-based language. Lambda functions are also known as lambda abstractions, which is a clear allusion to Alonzo Church's original invention's abstraction paradigm.
Any computation can be encoded using lambda calculus. Although it is Turing complete, it is quite pure and it does not maintain any states, in contrast to the idea of a Turing computer. Programming languages that are imperative adopt the model of computation based on states of Alan Turing whereas languages that are functional draw inspiration from lambda calculus.
Functional languages embrace a declarative style of programming that prioritises abstraction, composition, purity and data transformation, and directly inherit the lambda calculus philosophy (no state and no side effects). Haskell, Lisp, or Erlang are a few examples of functional languages. The Turing Machine, on the other hand, gave rise to imperative programming, which is used in Fortran, C, or Python like languages.
Although Python is not a functional language by nature, it did adopt some functional ideas early on. One such idea was the 'Lambda Function' which was added in 1994.
The anonymous nature of Python Lambda Functions indicates that they lack a name. As we already know, a standard Python function is defined using the def keyword. Similar to this, Python uses the lambda keyword to define an anonymous function. We'll study about Anonymous Functions further.
Let us look at some examples to explain the lambda function
>>> lambda x: x
In the above example a simple lambda function is been defined with the following components
lambda- The Keyword
x- Argument
x- body of the function or the one line expression
>>> lambda x: x * x
In this example the body of the function is given as x*x. So, the function returns the square of the argument passed to it.
>>> (lambda x: x * x)(3)
In the above example the output would be 9. How? The illustration is given below.
(lambda x: x * x)(3) = lambda 3: 3 * 3 = 3 * 3 = 9
Lambda can also be assigned to a variable and the function can be called with the help of that variable.
>>> sqfunc = lambda x: x * x >>> sqfunc(3) 9
In Lambda functions, multiple arguments are passed separated by commas and without parenthesis, unlike normal functions. For example
>>>add = lambda a,b : a + b >>>add(2,3) 5
An anonymous function is, simply, a function that has no name. With the lambda keyword, an anonymous function is constructed in Python. It could be given a name or not, to put it more lightly. Consider a lambda-defined anonymous function with two arguments that isn't bound to any variable.
>>> lambda x, y: x * y
The above function has two numbers as arguments and returns the product of these arguments. You don't need any name to be invoked in a python interpreter.
>>> _(3, 2) 6
Here the _ is used as it is bound to the expression that is last evaluated. Hence, the lambda function doesn't seriously lead to any practical use.
Another method is used to execute the Lambda function in python, immediately. This method is called the Immediately Invoked Function Expression(IIFE). This is mostly used in JavaScript.
>>> (lambda x, y: x * y)(3, 4) 12
The above function is a lambda function which is first defined and it is called immediately, after it is defined, with two arguments(3 and 4).
Python discourages the use of lambda expressions that are called immediately. It simply happens because a lambda expression can be called, as opposed to a typical function's body.
Lambda Functions are typically used in High Order Functions(functions in which one or more functions are passed as arguments or one or more functions are returned).
>>> highorder_function = lambda x, func: x + func(x) >>> highorder_function(5, lambda x: 10 / x) 7 >>> highorder_function(5, lambda x: x + 5) 15
Python lambdas are merely a shorthand notation if you're too lazy to construct a function, unlike other forms of lambda function in many other languages where they actually offer functionality.
But don't let that stop you from employing Python's lambda function. You could initially assume that a lambda function is just a function that is made easy to use by making the syntax to define the function or invoke it short. But the normal functions and lambda functions in python hold very nuance differences and similarities. We'll be looking into this relation in the following sections.
You might be wondering what actually differentiates a regular function from a lambda function. There is almost no difference as they perform the same function and use the same amount of resources. The only difference is that the name with which it is being called or addressed.
As mentioned earlier there are few differences between a normal function and regular function's syntax. A few characteristics which are present in Lambda functions differentiate it from the regular functions. They are:
1. Annotations are not supported.
2. Execution is done in single line.
3. It can't contain statements but only expressions. Statements like return, assert, pass, raise will raise an Error(SyntaxError).
4. It can invoked immediately(IIFE).
Similar to the regular function, Lambda function supports all types of arguments like positional arguments, named arguments, variable list of keyword arguments, variable list of arguments and keyword-only arguments
>>> (lambda x, y, z: x * y * z)(1, 2, 3) 6 >>> (lambda x, y, z=3: x * y + z)(1, 2) 5 >>> (lambda x, y, z=3: x * y + z)(1, y=2) 5 >>> (lambda *args: sum(args))(1,5,3) 9 >>> (lambda x, *, y=0, z=0: x + y + z)(1, y=5, z=3) 9
The decorator technique is used to implement the ability to add behaviour to a class or a function. A lambda can have a decorator applied to it. Although a lambda cannot be decorated using the syntax, @decorator, a decorator is merely a function and can therefore call a lambda function
Every free variable used in a function, with the exception of parameters, is tied to a particular value specified in the function's surrounding scope. This is known as a closure. Closures effectively define its environment in which they operate and can therefore be invoked from any location.
The concept of closures and lambdas are not related necessarily. But they can be closure functions.
def func(x): y = 4 return lambda z: x + y + z for i in range(3): closure = func(i) print(f"closure({i+5}) = {closure(i+5)}") OUTPUT: closure(5) = 9 closure(6) = 11 closure(7) = 13
lambda function in python can be tested similar to the normal functions. It is tested using both doctest and unitest. Let's try elaborating on this.
The modules in the unitest package handles the lambda functions of python similar to normal regular.
import unittest add = lambda x: x + 2 class TestLambda(unittest.TestCase): def test_add(self): self.assertEqual(add(2), 4) def test_add_two(self): self.assertEqual(add(2.2), 4.2) def test_add_three(self): # Should fail self.assertEqual(add(3), 6) if __name__ == '__main__': unittest.main(verbosity=2)
TestLambda defines test case with three methods to test, each of them executing a scenario to test the add() that has been implemented as the lambda function. The execution of this piece of code that has the TestLambda function yields this output:
$python unittest.py test_add_three (__main__.TestLambda) ... FAIL test_add_two (__main__.TestLambda) ... ok test_add_two(__main__.TestLambda) ... ok ====================================================================== FAIL: test_add_three (__main__.TestLambda) ---------------------------------------------------------------------- Traceback (most recent call last): File "unittest.py", line 18, in test_add_three self.assertEqual(addtwo(3), 6) AssertionError: 5 != 6 ---------------------------------------------------------------------- Ran 3 tests in 0.001s FAILED (failures=1)
The outcome of test add three is 5, however the anticipated outcome was 6. We have two test cases which were succesful and one failure, as predicted. The test case had a deliberate error that led to this failure. TestLambda will pass all of its tests if the anticipated result is changed from 6 to 5.
The modules in the doctest package extracts the code in python from docstring for the test execution. Although a normal docstring is not supported by the syntax of lambda functions in python, a string can be assigned to the __doc__ element of a lambda which has a name:
add = lambda x: x + 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)
When we execute the above code snippet we achieve the following results.
$ python doctest.py Trying: add(2) Expecting: 4 ok Trying: add(2.2) Expecting: 4.2 ok Trying: add(3) # Should fail Expecting: 6 ********************************************************************** File "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.
The yeilded results are as predicted. Though the doctest is possible, the syntax in python facilitates docstring for normal functions better than lambda functions.
The scenarios where the use of lambda function is valid is given bellow.
The lambda functions are used frequently with the functions that are built-in such as map(), filter(). This is illustrated in the code snippet below.
>>> list(map(lambda x: x.upper(), ['car', 'bus', 'bike'])) ['CAR', 'BUS', 'BIKE'] >>> list(filter(lambda x: 'b' in x, ['car', 'bus', 'bike'])) ['bus', 'bike'] >>> from functools import reduce >>> reduce(lambda acc, x: f'{acc} | {x}', ['car', 'bus', 'bike']) 'car | bus | bike'
Some other scenario where lambda functions can be useful are
->With key functions where the arguments of these functions can be a lambda function.
->Many UIFrameworks like Tkinter, wxPython or .NET Windows Forms with IronPython use this lambda function for many actions like mapping as a response to UI events.
->While using Python interpreter the use of lambda function provides various advantages.
This section illustrate a few scenarios of lambda usages that must be avoided. These examples might be scenarios where, in the context of lambda in python, the code exhibits some specific pattern as follows:
->It fails to follow the guide of the python style
->The difficulty in reading increases and gets too cumbersome.
->It gives some unnecassary output which makes readbility difficult
The scenarios are:
1. To raise and exception
2. At times the syntax of the lambda code might be difficult to decode and understand.
3. We can but should avoid writing lambda functions as classes.
Lambda functions do provide many advantages but at times it is adviced to avoid this function. So what else can be used instead of these functions. Some Highorder functions with a bit of manipulation can come handy at such times.
function is passed as the first parameter to the built-in function map(), which then applies that function to each item in the second argument, an iterable. Lists, tuples, strings, etc. are few examples of iterables.
>>> list(map(lambda x: x.capitalize(), ['bus', 'car', 'bike'])) ['Bus', 'Car', 'Bike']
Another common functional construct is the function filter() which is built-in, which can be transformed into a list comprehension. It accepts an iterable as a second argument after a predicate as the first argument. It creates an iterator that fulfils the predicate function and contains every component of the starting collection.
>>> even = lambda x: x%3 == 0 >>> list(filter(even, range(11))) [0, 3, 6, 9, 12, 15]
Reduce() is now a part of the functools module rather than being a built-in function. The first two arguments are an iterable and a function, similar to map() and filter().As a third parameter, it may also accept an initializer, which sets the value, that is initial, of the accumulator that results. Reduce() applies the function to each member of the iterable and accumulates the output which is returned only at the time where the iterable is finished.
This concludes our discussion of "Lambda Function in Python". I hope that you gained knowledge about this amazing function of python and are now more interested to dive into the language. You can consult the Python Tutorial for further guidance.
Good luck and happy learning!