With Python, it is possible to define anonymous functions using the lambda construct. With this, instead of using the "def" keyword, we can use the "lambda" keyword.
To illustrate, we provide an example below that defines a lambda function, funcAdd10(). This function takes one argument (x) and adds 10 to it. In fact, with lambda function, it is also possible to define the function and invoke it immediately. The last line in the following example does the same. We show the output in comments.
#Define a lambda function. funcAdd10 = lambda x: x + 10 #Invoke the lambda function. print(funcAdd10(70)) #Prints 80. #Define a lambda function and invoke it immediately. print((lambda x: x + 100)(180)) #Prints 280.
With lambda functions, we can have one function return another function. In fact, we can also have one function return a specific function from a series of functions.
Let us see an example to illustrate this behavior. In this example, "giveMeAFunction" function returns a lambda function depending upon the value of varName. We call giveMeAFunction() and then invoke the returned lambda function again.
#Define a lambda function. def giveMeAFunction(varName): if (varName == "even"): return lambda x: x % 2 == 0 elif (varName == "odd"): return lambda x: x % 2 != 0 #Invoke the returned functions. print(giveMeAFunction("even")(80)) # Prints True. print(giveMeAFunction("even")(237)) # Prints False. print(giveMeAFunction("odd")(237)) # Prints True. print(giveMeAFunction("odd")(80)) # Prints False.
Lambda functions are popularly used with Python functions like map(), filter(), and reduce(). These functions take another function as an argument along with a sequence of data. Instead of defining a function separately and passing its name to these functions, with lambda functions, we can simply define the function in these functions itself.
The map() function iterates through each element and calls the function for each of the element. Next, it builds a new list that contains the updated value from the original list. With Python3.0, this function provides an iterable object, instead of a list.
The filter() function iterates through all elements and builds a new list with only those elements of the original array that meet the specified criterion. The specified criterion is provided by the callback function. With Python3.0, this function also provides an iterable object, instead of a list.
The reduce() function accepts a function that applies its logic to each subsequent element of the list. Ultimately, it returns one final cumulative value. One way to think is that this method takes a list and returns a single value and hence, the name "reduce". We can also pass an optional third argument that represents a starting value. In Python 3, the reduce() function has been removed from the global namespace and has been kept in the functools module.
Here is a simple example that uses the above three functions to achieve basic functional programming. The map() function adds 100 to each elements of the original list and builds a new one. The filter() function selects only the odd values from the original list and once again, builds a new list. The reduce() function adds each element of the list and returns the cumulative value. For the reduce() function, we provide the optional third-value as 0.
from functools import reduce li = [25, 50, 75, 100, 125, 150] liAdded100 = map(lambda x: x + 100, li) print(type(liAdded100)) print("Let us print elements returned from map:") print(liAdded100) print("\nLet us print elements returned from filter:") liFilteredOdd = filter(lambda x: x % 2 != 0, li) print(type(liFilteredOdd)) print(liFilteredOdd) valReduced = reduce(lambda x, y: x + y, li, 0) print("\nLet us print the value returned from reduce: %s " % (valReduced))
We provide the output below.
<type 'list'> Let us print elements returned from map: [125, 150, 175, 200, 225, 250] Let us print elements returned from filter: <type 'list'> [25, 75, 125] <type 'int'> Let us print the value returned from reduce: 525
An important limitation of lambda functions is that the body can only be a single expression and not a block of statements. This choice puts an obvious limit on the amount of tasks that can be handled by lambda functions. This limitation is by design. The idea is that since lambda functions are a little harder to read, they should do only those tasks that are simple. For anything more sophisticated, we should resort to using regular named functions.
Accordingly, if we need a function such that its logic cannot be put in a one-line lambda function, then for each of the above calls, we can instead provide a named function. Thus, in our example we could build liAdded100 by explicitly passing a named function that adds 100 to the passed value. Something like this:
def funcAdd100(x): return (x + 100) li = [25, 50, 75, 100, 125, 150] liAdded100 = map(funcAdd100, li)
Before we conclude this discussion, we would like to mention that some of functions, like map() and filter(), can also be done using list comprehensions. In fact, it is one one of those topics, where you would find users defending both sides of the issue. The main argument against usage of lambda expressions is that they are less readable than list comprehensions.
If we were to use list comprehension, we could easily replace the lambda functions. The following example does exactly that:
from functools import reduce li = [25, 50, 75, 100, 125, 150] print("Let us print elements returned from map:") liAdded100 = [x + 100 for x in li] print(type(liAdded100)) print(liAdded100) print("\nLet us print elements returned from filter:") liFilteredOdd = [x for x in li if x % 2 != 0] print(type(liFilteredOdd)) print(liFilteredOdd)
And, here is the output:
Let us print elements returned from map: <type 'list'> [125, 150, 175, 200, 225, 250] Let us print elements returned from filter: <type 'list'> [25, 75, 125]