Python Arena
Step 1 of 3

Transforming with map

Most of your programs so far have worked by changing things, one step at a time: a loop adds to a total, append makes a list longer, a method changes an attribute of an object. Functional programming is a way of writing programs that works the other way round. It computes new values from old ones with functions, and it leaves the old values as they are. A way of writing programs like this is called a programming paradigm. Here's one small job done both ways:

numbers = [3, 8, 5]

doubled = []
for number in numbers:
    doubled.append(number * 2)
print(doubled)

print([number * 2 for number in numbers])
Output
[6, 16, 10]
[6, 16, 10]

The loop changes the list doubled in every round, so to know what it holds at some point, you follow the rounds. The comprehension is one expression that says what the new list is, and nothing changes on the way. That's the functional way of thinking, and you've used it since comprehensions. This step and the next two show three classic functional tools: map, filter and reduce. Each of them takes a function as an argument, just as sorted takes a key.

map

The function map calls a function on every item of a series and gives the results. You write it as map(function, series):

texts = ["12", "-4", "30"]
numbers = map(int, texts)
for number in numbers:
    print(number * 2)
Output
24
-8
60

The function can be any function that takes one argument: one of Python's own, one you wrote, or a lambda:

def initial(name):
    return name[0] + "."


names = ["Ada", "Linus", "Grace"]
print(list(map(initial, names)))
print(list(map(lambda name: name.upper(), names)))
Output
['A.', 'L.', 'G.']
['ADA', 'LINUS', 'GRACE']

A list comprehension does the same: [initial(name) for name in names]. Neither is more correct than the other, so choose whichever reads better to you.

map gives an iterator

map doesn't return a list. It returns a map object, an iterator: like a generator, it computes each value only when it's asked for, and it's used up after one pass. Printing it shows only <map object at 0x7f3a2c>, which is why the example above wraps it in list(). Going through the same map twice gives nothing the second time:

prices = [2.0, 3.5, 10.0]
with_tax = map(lambda price: price * 1.25, prices)

for price in with_tax:
    print(price)
print("Once more:")
for price in with_tax:
    print(price)
Output
2.5
4.375
12.5
Once more:

When you need the values more than once, store a list instead: with_tax = list(map(...)).

map and your own objects

map works with any items, objects of your own classes too. The function gets one object and can compute anything from it, such as a number or a line of text made from its attributes:

class Room:
    def __init__(self, name, width, length):
        self.name = name
        self.width = width
        self.length = length


rooms = [Room("kitchen", 3, 4), Room("hall", 1.5, 6), Room("bedroom", 4, 3.5)]
print(list(map(lambda room: room.width * room.length, rooms)))
print(list(map(lambda room: f"{room.name}: {room.width} x {room.length} m", rooms)))
Output
[12, 9.0, 14.0]
['kitchen: 3 x 4 m', 'hall: 1.5 x 6 m', 'bedroom: 4 x 3.5 m']

Your task

Challenge 1 of 3

Fill in the blanks so that the program converts the temperatures to Fahrenheit with map and prints them as a list:

[32.0, 77.0, 212.0]

Challenge 2

What does this program print? Type each line it prints.

Challenge 3

The class Product holds a product's name and price. Write a function price_tags(products) that uses map to return a list of price tags, one string for each product: the name, a colon and a space, and the price with two decimals. For example,

products = [Product("tea", 3.5), Product("honey", 7), Product("lemon", 0.45)]
print(price_tags(products))

should print:

['tea: 3.50', 'honey: 7.00', 'lemon: 0.45']