processing a tuple in lambda expression in list comprehensions

Viewed 3780

I am learning python and was just fiddling with the lambda expressions and list comprehensions when I realized that:

>>> list(map(lambda x: x*x, [1,2]))

outputs >>> 1,4 but

>>> list(map(lambda (x): x*x, [1,2])) shows an error pointing at parenthesis around x

It appears that the lambda expression cannot have a parenthesis around its parameters list. (Checked in python 3.x)

I now want to ask that if I cannot add a parenthesis around the parameters in the lambda expression, then how can I process a tuple using a map and a lambda expression. for eg:

>>> records = [('bob', 35, 'manager'), ('sue', 40, 'developer')]
>>> list(map((lambda (name, age, job): age), records))

this statement shows a syntax error indicating the parenthesis before the name parameter. I then tried an expression without the parenthesis:

>>> list(map((lambda name, age, job: age), records))

But this statement also shows an error saying that lambda expression needs 3 arguments but only 1 was provided.

I get that the map function is pulling out an entire record tuple from records list and passing it to the lambda function, and the lambda function is taking this entire tuple record as an argument to the name parameter.

I want to know, How can I process a tuple in a lambda expression. Also please tell me why is it written so in the book (Learning python by Mark Lutz) that this works? Is it for python 2.x. I am new to python. Thanks in advance.

3 Answers

You can use the special unpacking syntax with lambdas in Python2 only. However, you can use itertools.starmap to accomplish the unpacking of each item in records as it is passed to the function under the hood in Python3:

import itertools
records = [('bob', 35, 'manager'), ('sue', 40, 'developer')]
print(list(itertools.starmap(lambda name, age, job: age, records)))

Output:

[35, 40]

The syntax you are attempting works in Python 2.x. It won't work in Python 3.

In Python 3, you can use positional indexing:

from operator import itemgetter

records = [('bob', 35, 'manager'), ('sue', 40, 'developer')]

ages1 = list(map(lambda x: x[1], records))   # [35, 40]
ages2 = list(map(itemgetter(1), records))    # [35, 40]

Better (more efficient, more readable) is to abandon lambda altogether. You can use a list comprehension with sequence unpacking:

ages3 = [age for _, age, _ in records]       # [35, 40]

You can even use zip if you don't mind a tuple:

_, ages4, _ = zip(*records)                  # (35, 40)

For a cleaner version of the last solution, you can use itertools.islice:

from itertools import islice

ages5 = next(islice(zip(*records), 1, 2))    # (35, 40)

Why not just

list(map((lambda x:x[1]), records))
Related