Bottom Page

Thread Rating:
  • 1 Vote(s) - 5 Average
  • 1
  • 2
  • 3
  • 4
  • 5
 Lambda: How, Why, and Why not.
When I first started learning python I found lambda extremely confusing.  It isn't that there is anything particularly complicated about it; but something about the syntax--as well as a name that really didn't mean anything to me--just didn't click.

What it is:
In python, lambda allows one to define simple functions in-line.  It is syntactically an expression, not a statement (a function definition using def on the other hand is a statement).  Understanding what this means is critical and I will return to it shortly.  You will also often hear it said that with lambda, one can write anonymous functions.  But before we get to that...

The name:
Why is lambda, called lambda?  Well, it comes from a field of mathematical-logic called Lambda Calculus and was further popularized by languages such as LISP.  The ideas associated with lambda are generally equated with the paradigm of programming languages known as functional programming languages.
With regards to the name, even Guido van Rossum lamented:
Guido van Rossum Wrote:I was never all that happy with the use of the "lambda" terminology, but for lack of a better and obvious alternative, it was adopted for Python.
For more on the decisions to add functional language features, read here:
Origins of Pythons “Functional” Features

How it works:
The syntax of lambda actually isn't that complicated.  It takes this structure:
lambda arguments : return_value
So to put it in words, we need the keyword lambda; followed by a list of arguments; a colon; and then our return value.

Let's look at a simple (and largely useless) example:
square = lambda x: x**2
The above code is absolutely identical to:
def square(x):
    return x**2
It creates a function which takes a single argument, and returns that number squared.  It is called like any other function:
>>> square(5)
Additional arguments can be used by separating them with commas, as in:
>>> pythag = lambda x,y : (x**2+y**2)**(0.5)
>>> pythag(5,5)
You can even--if you are so inclined--make use of *args and **kwargs:
>>> sum_of_squares = lambda *args: sum(arg**2 for arg in args)
>>> sum_of_squares(4,6,2)
But why do we need them:
Hopefully, at this point, you are asking yourself this question.  Usually the answer to this is, we don't.  In fact if you are using lambda there should be a very good reason.  Writing short little functions as just demonstrated, is not a good enough reason.  

The two primary reasons for using lambda are:
  1. You are writing a function that returns a function.
  2. You are writing a short callback function.
Let's look at number one; functions that return functions.  Let's modify our previous square function so that we can use any power we want:
def power_of(power):
    return lambda x: x**power
We now have a function that returns a lambda.  And this is how we use it:
>>> square = power_of(2)
>>> cube = power_of(3)
>>> fourth = power_of(4)
>>> square(8)
>>> cube(8)
>>> fourth(8)
At this point we get our first glimpse of what "Anonymous function" means.  The lambda function that was returned was never actually assigned to a name.  

I would also like to note at this point something that a lot of people seem to miss when they start out.  We didn't need to use lambda for this function at all.  The previous power_of function is identical to this:
def power_of(power):
    def pow_it(x):
        return x**power
    return pow_it
In this version we explicitly defined a function within our power_of function; and then returned it.  The enclosed function is no longer anonymous.

Now let's look at the second reason I listed for using lambda; you are writing a callback function.  Basically a callback function is a function that you pass as an argument to another function.  The most commonly seen example of this is with regards to sort/sorted.

Take the following list of tuples:
data = [(0,4), (5,3), (5,2), (6,1), (7,9), (0,12), (2,1)]
If we sort this list (either by the sorted builtin or the list.sort method) we get the following:
>>> print(sorted(data))
[(0, 4), (0, 12), (2, 1), (5, 2), (5, 3), (6, 1), (7, 9)]
The items by default sorted with preference for the first item; but what if we wanted to sort by the second item?  Well, the sort/sorted methods take an optional key argument.  This key is a callback function dictating how the list should be sorted.

We could manually write a callback function and use it:
def order_by_second(item):
    return item[1]
>>> print(sorted(data, key=order_by_second))
[(6, 1), (2, 1), (5, 2), (5, 3), (0, 4), (7, 9), (0, 12)]
But most agree this is a bit silly.

The accepted way to pass simple callbacks, is by using lambda:
>>> print(sorted(data, key=lambda item:item[1]))
[(6, 1), (2, 1), (5, 2), (5, 3), (0, 4), (7, 9), (0, 12)]
This is when understanding the distinction between statement and expression becomes important.  As def is a statement, we could never actually define a function while passing it as an argument at the same time.  Statements can not appear in such locations.  The fact that lambda is an expression gives us the freedom we need to anonymously create and pass simple functions such as this.  Along with being able to pass these functions immediately as arguments, you also have the freedom to do other things like place them in dictionaries.

For example, this code in which three simple functions are defined and placed in a dictionary:
def rook(x,y):
    return x+y

def queen(x,y):
    return max(x,y)

def knight(x,y):
    return max((x//2+x%2),(y//2+y%2))

HEURISTICS = {"rook"   : rook,
              "queen"  : queen,
              "knight" : knight}
Would become as short and simple as this:
HEURISTICS = {"rook"   : lambda x,y : x+y,
              "queen"  : lambda x,y : max(x,y),
              "knight" : lambda x,y : max((x//2+x%2),(y//2+y%2))}
While lambda can be a very neat tool, knowing when it is appropriate to use a lambda is just as important as knowing when not to.  You would do well to be aware that Guido van Rossum strongly considered removing lambda from the language completely when Python 3 was first being released.  He certainly has justified reasons for this.


Top Page

Forum Jump:

Users browsing this thread: 1 Guest(s)