Syntactical sugar in Python: map, filter, reduce vs. the python way

This article is going to try to shed some light on how to convert map, reduce and filter (often used with lambda expressions) in python into a syntax conforming to „the python way“. At the bottom you will find a download link to execute this stuff for yourself.

l = range(10)
print l
#[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

This is going to be the list (l) we’re working on.

What we’ll be looking at first, is iterating through this list and applying a function. And what simpler function could there be, than the identity?
a = map(lambda x: x, l)
b = list(x for x in l)
print a, b
#[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

If you don’t really know what the we did in (a) you may want to read up on lambda expressions, a topic I am not covering now. We need the list-method in (b) because map() always returns a list and the for-expression coughs up a generator object which we have to fully evaluate and convert to a list, to get the equivalent. Generator objects I will cover in the future. For now, suffice to say, it has a reason we are applying the list-function.

Next, we are doing an actual calculation by looking for the cubes of our list (l)
a = map(lambda x: x**2, l)
b = list(x**2 for x in l)
print a, b
#[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

Of course you can use actual functions like the cube root or any user defined function.
def usr(p):
return p+p
a = map(lambda x: usr(x), l)
b = list(usr(x) for x in l)
#[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]

We could also combine various functions to create a new anonymous function to apply to our list
a = map(lambda x: usr(x)+5, l)
b = list(usr(x)+5 for x in l)
#[5, 7, 9, 11, 13, 15, 17, 19, 21, 23]

I think you got the idea. Remember, (a) and (b) are equivalent.

Next we will use two inputs. Let’s get ourselves another list then, shall we?
m = range(10,20)
#[10, 11, 12, 13, 14, 15, 16, 17, 18, 19]

And we add both lists together
a = map(lambda x,y: x+y, l, m)
b = list(x+y for x,y in zip(l, m))
print a,b
#[10, 12, 14, 16, 18, 20, 22, 24, 26, 28]

Why internally those constructs do something very different, I will cover in the article about generators. But the resulting list is completely the same. Zip() takes lists/iterables as input and convertes them to a list of tuples (n-tuples for n = [number of input iterables]). This tuple is taken apart and put into x and y.

And we can also look at other methods like filter
a = filter(lambda x: x%2==0, l)
b = list(x for x in l if x%2==0)
#[0, 2, 4, 6, 8]

filter() can only filter through input. If you want to apply apply a function, you will have to use map again. In (b) you can do that intuitively:

a = map(lambda x: x*2, filter(lambda x: x%2==0, l))
b = list(x*2 for x in l if x%2==0)
print a,b
#[0, 4, 8, 12, 16]

As we can see, the for-construct can apply map() and filter()

reduce, which applies a two-parameter-function accumulatively throughout an iterable, on the other hand, is looks a bit different on converting (we try to find out the sum of l):
a = reduce(lambda x,y: x+y, l, 0)
b = 0
for x in l:
b = b+x

If we use for in this way, we also can use the filter-if and the map, but we can not use it in the same line as the for-construct. But it is rather trivial, where to use it.

a = reduce(lambda x,y: x+y, map(lambda x: x*2, filter(lambda x: x%2==0, l)), 0)
b = 0
for x in l:
if (x%2==0):
b = b+ x*2

In some cases map, filter, and reduce, (together with lambda expressions) probably are syntactic sugar. But in most cases, the python way looks much more readable.

As a note: you can use map, filter and reduce without lambda, as long as you have a function that has as many parameters as you have input iterables. This is valid and does the same as the one far above:
a = map(usr, l)
#[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]

And if you do not want to think about lambdas you can easily create named functions with this simple rule:
lambda arguments: expression
#is the same as
def name(arguments):
return expression

As a final note: The for-construct actually is a generator expression which are handled a bit differently from lists or other „normal“ iterables. This can have impact on execution speed (or control flow in general) but that will be covered later.
Thanks for reading.

Attachment: lambda_anon.txt which I couldn’t name .py because WordPress is paranoid.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.

Diese Website verwendet Akismet, um Spam zu reduzieren. Erfahre mehr darüber, wie deine Kommentardaten verarbeitet werden.