Python's map(): Processing Iterables Without a Loop – Real Python

By Real Python

Sometimes you might face situations in which you need to perform the same operation on all the items of an input iterable to build a new iterable. The quickest and most common approach to this problem is to use a Python for loop. However, you can also tackle this problem without an explicit loop by using map().

In the following three sections, you’ll learn how map() works and how you can use it to process and transform iterables without a loop.

map() loops over the items of an input iterable (or iterables) and returns an iterator that results from applying a transformation function to every item in the original input iterable.

According to the documentation, map() takes a function object and an iterable (or multiple iterables) as arguments and returns an iterator that yields transformed items on demand. The function’s signature is defined as follows:

map(function, iterable[, iterable1, iterable2,..., iterableN])

map() applies function to each item in iterable in a loop and returns a new iterator that yields transformed items on demand. function can be any Python function that takes a number of arguments equal to the number of iterables you pass to map().

Note: The first argument to map() is a function object, which means that you need to pass a function without calling it. That is, without using a pair of parentheses.

This first argument to map() is a transformation function. In other words, it’s the function that transforms each original item into a new (transformed) item. Even though the Python documentation calls this argument function, it can be any Python callable. This includes built-in functions, classes, methods, lambda functions, and user-defined functions.

The operation that map() performs is commonly known as a mapping because it maps every item in an input iterable to a new item in a resulting iterable. To do that, map() applies a transformation function to all the items in the input iterable.

To better understand map(), suppose you need to take a list of numeric values and transform it into a list containing the square value of every number in the original list. In this case, you can use a for loop and code something like this:

>>> numbers = [1, 2, 3, 4, 5]
>>> squared = [] >>> for num in numbers:
...  squared.append(num ** 2)
... >>> squared
[1, 4, 9, 16, 25]

When you run this loop on numbers, you get a list of square values. The for loop iterates over numbers and applies a power operation on each value. Finally, it stores the resulting values in squared.

You can achieve the same result without using an explicit loop by using map(). Take a look at the following reimplementation of the above example:

>>> def square(number):
...  return number ** 2
... >>> numbers = [1, 2, 3, 4, 5] >>> squared = map(square, numbers) >>> list(squared)
[1, 4, 9, 16, 25]

square() is a transformation function that maps a number to its square value. The call to map() applies square() to all of the values in numbers and returns an iterator that yields square values. Then you call list() on map() to create a list object containing the square values.

Since map() is written in C and is highly optimized, its internal implied loop can be more efficient than a regular Python for loop. This is one advantage of using map().

A second advantage of using map() is related to memory consumption. With a for loop, you need to store the whole list in your system’s memory. With map(), you get items on demand, and only one item is in your system’s memory at a given time.

Note: In Python 2.x, map() returns a list. This behavior changed in Python 3.x. Now, map() returns a map object, which is an iterator that yields items on demand. That’s why you need to call list() to create the desired list object.

For another example, say you need to convert all the items in a list from a string to an integer number. To do that, you can use map() along with int() as follows:

>>> str_nums = ["4", "8", "6", "5", "3", "2", "8", "9", "2", "5"] >>> int_nums = map(int, str_nums)
>>> int_nums
<map object at 0x7fb2c7e34c70> >>> list(int_nums)
[4, 8, 6, 5, 3, 2, 8, 9, 2, 5] >>> str_nums
["4", "8", "6", "5", "3", "2", "8", "9", "2", "5"]

map() applies int() to every value in str_nums. Since map() returns an iterator (a map object), you’ll need call list() so that you can exhaust the iterator and turn it into a list object. Note that the original sequence doesn’t get modified in the process.

You can use any kind of Python callable with map(). The only condition would be that the callable takes an argument and returns a concrete and useful value. For example, you can use classes, instances that implement a special method called __call__(), instance methods, class methods, static methods, and functions.

There are some built-in functions that you can use with map(). Consider the following examples:

>>> numbers = [-2, -1, 0, 1, 2] >>> abs_values = list(map(abs, numbers))
>>> abs_values
[2, 1, 0, 1, 2] >>> list(map(float, numbers))
[-2.0, -1.0, 0.0, 1.0, 2.0] >>> words = ["Welcome", "to", "Real", "Python"] >>> list(map(len, words))
[7, 2, 4, 6]

You can use any built-in function with map(), provided that the function takes an argument and returns a value.

A common pattern that you’ll see when it comes to using map() is to use a lambda function as the first argument. lambda functions are handy when you need to pass an expression-based function to map(). For example, you can reimplement the example of square values using a lambda function as follows:

>>> numbers = [1, 2, 3, 4, 5] >>> squared = map(lambda num: num ** 2, numbers) >>> list(squared)
[1, 4, 9, 16, 25]

lambda functions are quite useful when it comes to using map(). They can play the role of the first argument to map(). You can use lambda functions along with map() to quickly process and transform your iterables.

If you supply multiple iterables to map(), then the transformation function must take as many arguments as iterables you pass in. Each iteration of map() will pass one value from each iterable as an argument to function. The iteration stops at the end of the shortest iterable.

Consider the following example that uses pow():

>>> first_it = [1, 2, 3]
>>> second_it = [4, 5, 6, 7] >>> list(map(pow, first_it, second_it))
[1, 32, 729]

pow() takes two arguments, x and y, and returns x to the power of y. In the first iteration, x will be 1, y will be 4, and the result will be 1. In the second iteration, x will be 2, y will be 5, and the result will be 32, and so on. The final iterable is only as long as the shortest iterable, which is first_it in this case.

This technique allows you to merge two or more iterables of numeric values using different kinds of math operations. Here are some examples that use lambda functions to perform different math operations on several input iterables:

>>> list(map(lambda x, y: x - y, [2, 4, 6], [1, 3, 5]))
[1, 1, 1] >>> list(map(lambda x, y, z: x + y + z, [2, 4], [1, 3], [7, 8]))
[10, 15]

In the first example, you use a subtraction operation to merge two iterables of three items each. In the second example, you add together the values of three iterables.