07Oct
artwork depicting a stylized map() Python function
Mapping the knowledge onto you

We’re excited to continue our “Python Explained” series with another article — this time we’re taking a look at the map() function. Its goal may appear rather simple (help the developer work with large sets of data), but there’s always more to learn. Here are some of the Python-focused content we produced as of late:

Last but not least, do check our Python interview questions out! In this article, we’ll examine the inner workings of the map() function in Python. As always, we’re using Python 3.

Before We Begin…

In theory, articles like these shouldn’t be necessary — if you encounter a function you don’t understand well, the go-to source of knowledge is supposed to be the Python built-in help() module.

C:\Python\Test_folder> Python
Python 3.7.0 (v3.7.0:1bf9cc5093, Jun 27 2018, 04:06:47) [MSC v.1914 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>>  

You pass the function you want to learn about as the argument…

>>> help(map)

… and get an explanation that (hopefully) clears up all misunderstandings. Here’s what the Python help() module has to say about map():

Make an iterator that computes the function using arguments from each of the iterables. Stops when the shortest iterable is exhausted.

Well, the folks at the Python Foundation have condensed a lot of information into this neat little description — so much so that many novice Python programmers have a hard time understanding how they can apply map() in their programs. It probably looks like this to a beginner Python programmer:

artwork depicting a stylized terminal interface
Quite easy to follow, don’t you think?

However, the explanation above introduces two terms which are essential to Python: iterator and iterable. Since they appear frequently throughout various Python documentation files, let’s explain them now:

  • Iteration is a general computer science term; it refers to performing an action to a set of elements, one element at a time. A good example is the loop — it works with each individual item until the whole set of items is exhausted.
  • An Iterable is an object that we can loop over (e.g. a string, a list, or a file). From the Iterable, we get the iterator.
  • An iterator is an object that represents a stream of data; it returns the data one element at a time. It also remembers its position during iteration. Essentially, it governs how the iterable object should be iterated over.

With these terms defined, we can now move to the next point — analyzing what map() does and how it can be applied.

So What Does map() Actually Do in Python?

The map() function applies the function you provide to each item in an iterable. The result is a map object which is an iterator. Here’s the syntax of map():

map(func, *iterables)

Without map(), we’d be forced to write complex code to “loop” the given function over a number of items. Let’s imagine a neat little experiment: we have a list of 10 words.

test_list = ["effort", "circle", "yearly", "woolen", "accept", "lurker",
            "island", "faucet", "glossy", "evader"]

We suspect that some of them may be abcderian (i.e. the letters appear in alphabetical order). We write a function called is_abecedarian to check if the given word is abcderian:

def is_abecedarian(input_word):
    index = 0
    for letter in input_word[0:-1]:
        if ord(input_word[index]) > ord(input_word[index + 1]):
            return False
        else:
            index += 1
    return True

Now, we want to apply our function to the word list and create a new list which will hold True and False values, signifying whether some of the words are indeed abcderian. One method would involve initializing a new list, then using a for loop to iterate over the list elements:

value_list = []
for item in test_list:
    value = is_abecedarian(item)
    value_list.append(value)

This will output:

[True, False, False, False, True, False, False, False, True, False]

Thanks to map(), however, we can reduce the code above to a neat little one-liner:

map(is_abecedarian, test_list)

However, we should be mindful that map() doesn’t return a list — it returns a map object instead.

You might be curious which words are actually abcderian — let’s code the answer to your question:

for item in test_list:
    if is_abecedarian(item):
        print(f"The word '{item}' is abecedarian. :)")
    else:
        print(f"The word '{item}' is not abecedarian. (")

This will output:

The word 'effort' is abecedarian. :)
The word 'circle' is not abecedarian.
The word 'yearly' is not abecedarian.
The word 'woolen' is not abecedarian.
The word 'accept' is abecedarian. :)
The word 'lurker' is not abecedarian.
The word 'island' is not abecedarian.
The word 'faucet' is not abecedarian.
The word 'glossy' is abecedarian. :)
The word 'evader' is not abecedarian.

We can also visualize this explanation to help you understand it even better:

artwork depicting how the map() function in Python works
Almost feels like magic

It’s also useful to define map and mapping — we can use the definitions provided by Allen B. Downey in his Think Python book:

  • map: A processing pattern that traverses a sequence and performs an operation on each element.
  • mapping: A relationship in which each element of one set corresponds to an element of another set.

Converting map() into a List, Tuple, and a Set

Since map() doesn’t return a list/tuple/set, we need to take an extra step to convert the resulting map object:

def capitalize_word(input_word):
    return input_word.capitalize()


map_object = map(capitalize_word, ['strength', 'agility', 'intelligence'])
test_list = list(map_object)
print(test_list)

map_object = map(capitalize_word, ['health', 'mana', 'gold'])
test_set = set(map_object)
print(test_set)

map_object = map(capitalize_word, ['armor', 'weapon', 'spell'])
test_tuple = tuple(map_object)
print(test_tuple)

This will output:

['Strength', 'Agility', 'Intelligence']
{'Mana', 'Health', 'Gold'}
('Armor', 'Weapon', 'Spell')

Combining map() with Lambda Expressions

artwork depicting how lambda expressions can be used with the map() function
From multiple lines to a pretty one-liner

Lambda expressions are a fine addition to our arsenal: combining them with map() makes your Python program even smaller, code line-wise. Lambda expressions create anonymous functions, i.e. functions that aren’t bound to a specific identifier. Conversely, creating a function via the def keyword bounds the function to its unique identifier (e.g. def my_function creates an identifier my_function).

However, lambda expressions have a set of limitations: each of them can only do one thing and only be used on one place. They’re often used in conjunction with other functions, so let’s see how can we utilize them and map() simultaneously. For instance, we can turn the code below…

cities = ["caracas", "bern", "oslo", "ottawa", "bangkok"]


def capitalize_word(input_word):
    return input_word.capitalize()


capitalized_cities = map(capitalize_word, cities)

… into a more concise version:

cities = ["caracas", "bern", "oslo", "ottawa", "bangkok"]

capitalized_cities = map(lambda s: s.capitalize(), cities)

However, there’s a caveat: both map() and lambda expressions provide the ability to condense multiple-line code into a single line. While this functionality is pretty awesome, we need to keep in mind one of the golden rules of programming: Code is read more often than it is written. This means that both map() and lambda expressions can improve code brevity, but sacrifice code clarity. Sadly, there aren’t really any clear guidelines on how readable your code should be — you’ll figure this yourself out as your programming experience grows.

Using map() to Iterate over a Dictionary

Naturally, map() is well-suited for scenarios when you want to iterate over a dictionary. Let’s imagine we have a dictionary containing the prices of apples, pears, and cherries. We need to update the price list by applying a 15% discount to it. Here’s how we can do it:

price_list = {
    "pear": 0.60,
    "cherries": 0.90,
    "apple": 0.35,
}


def calulates_discount(item_price):
    return (item_price[0], round(item_price[1] * 0.85, 2))


new_price_list = dict(map(calulates_discount, price_list.items()))

The output will be:

{'pear': 0.51, 'cherries': 0.77, 'apple': 0.3}

Combining map() with Lambda Expressions to Iterate over a Dictionary

Programming is especially interesting when you start to combine multiple functions — a good example is using map() and lambda expressions simultaneously to iterate over a dictionary. In the code snippet below, we initialize a list of dictionaries and pass each dictionary as a parameter to the lambda function.

list_of_ds = [{'user': 'Jane', 'posts': 18}, {'user': 'Amina', 'posts': 64}]

map(lambda x: x['user'], list_of_ds)  # Output: ['Jane', 'Amina']

map(lambda x: x['posts'] * 10, list_of_ds)  # Output: [180, 640]

map(lambda x: x['user'] == "Jane", list_of_ds)  # Output: [True, False]

map() Alternatives: List Comprehension

Like with all technologies/products/techniques/approaches/etc., some Python developers consider the map()() function to be somewhat un-Pythonic (i.e. not following the spirit and design philosophies of how Python programs should be built). They suggest using list comprehensions instead so that

map(f, iterable)

turns into

[f(x) for x in iterable]

Speed- and performance-wise, map() and list comprehensions are roughly equal, so it’s highly unlikely that you’ll see a significant decrease in execution times — experienced Python developer Wesley Chun addressed this question in his talk Python 103: Memory Model & Best Practices (the discussion starts around 5:50 if the timestamp didn’t work correctly).

Conclusion

Phew — turns out this map() function in Python isn’t so complicated after all! To solidify your knowledge, however, you should play around with these examples and tweak them — by breaking and fixing these code snippets, you may very well run into certain aspects of the map() function that we didn’t cover. Good luck! 🙂

Leave a Reply