A few weeks ago, I introduced you to functional programming in Python. Today, I'd like to go further into this topic and show you so more interesting features.

Lambda Functions

What do we call lambda functions? They are in essence anonymous functions. In order to create them, you must use the lambda statement:

>>> lambda x: x
<function <lambda> at 0x102e23620>

In Python, lambda functions are quite limited. They can take any number of arguments; however they can contain only one statement and be written on a single line.

They are mostly useful to be passed to high-order functions, such as map():

>>> list(map(lambda x: x * 2, range(10)))
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]

This will apply the anonymous function lambda x: x * 2 to every item returned by range(10).

functools.partial

Since lambda functions are limited to being one line long, it's often that they are used to specialize longer version of an existing function:

def between(number, min=0, max=1000):
    return max > number > min

# Only returns number between 10 and 1000
filter(lambda x: between(x, min=10), range(10000))

Our lambda is finally just a wrapper of the between function with one of the argument already set. What if we would have a better way, without the various lambda limitations, to write that? That's where functools.partial comes handy.

import functools
def between(number, min=0, max=1000):
    return max > number > min

# Only returns number between 10 and 1000
atleast_10_and_upto = functools.partial(between, min=10)
# Return number betweens 10 and 1000
filter(atleast_10_and_upto, range(10000))

# Return number betweens 10 and 20
filter(lambda x: atleast_10_and_upto(x, max=20), range(10000))

The functools.partial function returns a specialized version of the between function, where min is already set. We can store them in a variable, use it, reuse it, as much as we want. We can pass it a max argument, as shown in the second part — using a lambda! You can mix and matches those two as you prefer and what seems clearer for you.

Common lambda

There is a type of lambda function that is pretty common: the attribute or item getter. They are typically used a key function for sorting or filtering.

Here's a list of 200 tuples containing two integers (i1, i2). If you want to use only i2 as the sorting key, you would write:

mylist = list(zip(range(40, 240), range(-100, 100)))

sorted(mylist, key=lambda i: i[1])

Which works fine, but make you use lambda. You could rather use the operator module:

import operator

mylist = list(zip(range(40, 240), range(-100, 100)))

sorted(mylist, key=operator.itemgetter(1))

This does the same thing, except it avoids using lambda altogether. Cherry-on-the-cake: it is actually 10% faster on my laptop.

I hope that'll make you write more functional code!