Replacing map() With Pythonic Code
00:00
In many cases, you can replace a call to the map()
function using either a list comprehension or a generator expression. Let’s compare all three.
00:10
We’ve seen how the map()
function works. We have as a first argument the function that we want to map at every element of an iterable. So, in this case, we only have one iterable, which means that the function in the map()
call has only one positional argument.
00:26 We know that this is going to return a map object.
00:30 Now, what you’ll do with this map object depends on what your application asks for. If you don’t want to necessarily generate a list with this map object and maybe all you want to do is iterate over the object, then you’ll probably want to just keep it like this as a map object and pass it on to whatever code that needs to iterate over it. Now, the list comprehension, which you’ve probably seen before, is going to return or generate a list.
00:57
The basic idea is that you’re building this list incrementally, and so you can sort of shorten this instead of using the typical way where you would first initialize an empty list and then append to it the values of the function at every element x
of the iterable—you could do all of that, say, with this one line, and this is the list comprehension. And then lastly, the generator expression. It’s going to return a generator object, which is also an iterator, just like the map object is.
01:30 The only difference in the syntax between the list comprehension and the generator expression is that the square brackets in the list comprehension are just parentheses in the generator expression. And so, again, the generator expression is going to return an iterator and it’s going to function pretty much in the same way as the iterator returned by the map object.
01:55 Now, you’re probably already using list comprehensions and maybe even also generator expressions. List comprehensions are very popular among Python developers, and so if you want to write your programs so that they look more like what is familiar to other Python developers, you may want to use a list comprehension or maybe even a generator expression.
02:18
Ultimately, what you decide to do—whether you want to use the map()
function or a list comprehension or a generator expression—may depend on what’s your application and what you’re trying to do.
02:28
The list comprehension and the generator expressions, they’re a bit more explicit and they read almost like pure English. “We want to generate a list of the values of the function at each element x
of the iterable.”
02:43
Now, one situation where the map()
function may actually be a little bit more readable is when the function that you want to map takes on more than one positional argument. So in this example, this function here takes on two positional arguments and those are being fed in by one element from the iterable_1
and another one from the iterable_2
, one at a time.
03:05
We’ve seen this type of code before. Now, to generate an iterator using a generator expression, we would need the following code here. We would first need to zip the iterable_1
and the iterable_2
. This will return an iterator and the iterator will yield, one at a time, one element from the iterable_1
and the corresponding element from the iterable_2
.
03:29
And this will return, on demand, an iterable containing two elements, and then we go ahead and unpack those one at a time using the variables x
and y
, and those are the values or the arguments that we’re feeding into the function.
03:44
So, this generator expression is pretty explicit. It’s telling us exactly what we’re sort of needing to do. We need to combine these two iterables and generate a tuple, one at a time, of the (x, y)
values, and again feeding it into the function()
.
04:02
Now, suppose instead we had a function that took on, say, three positional arguments. So, with three iterables the code with the map()
function would be pretty much the same, right?
04:14
If function()
takes on three positional arguments, we would have then therefore a third iterable. And this time instead for the generator expression, we may want to use the unpacking operator (*
).
04:27
So instead of doing the unpacking over here in the for
statement that are returned by the zip()
iterator, we would just unpack it into the function()
and just save a little bit of code that way. Now, personally, in this case, I would probably prefer the map()
call over this sort of longer generator expression, but really it all depends on what you want to do and how you think about what you’re doing in the particular problem that you’re trying to solve.
04:57
Let’s do a quick example where we compare the use of the map()
function versus, say, a generator expression. So, say you have some functions.
05:06
Say it is a function that takes three numbers and returns the sum of the cubes of the three numbers. This function, we’ll call it sum_of_cubes()
. And again, it takes three numbers, say x
, y
, z
, and it just simply returns the sum of the cubes of x
, y
, and z
.
05:26
Let’s create three lists that will contain values of x
’s and y
’s and z
’s, and we’ll call these lists x_iterator
—or x_it
, y_it
, and z_it
.
05:39 And these I’ll just bring up from my history in the REPL, and you can really put down here any lists that you want and they don’t even have to have the same number of elements, but we’ll keep it simple and have it so that each list contains three elements.
05:55
So, let’s go ahead and call the map()
function on this, and let’s save that in a variable m
. This will be map()
. We want to map the sum_of_cubes()
function using the x_it
iterable, the y_it
, and the z_it
.
06:14
We’ll save that in m
and then we’ll use a generator expression. So in this case, we’re going to call the sum_of_cubes()
function on the values x
, y
, and z
, and we want to do this for x
, y
, and z
in zip()
, and this is x_it
, y_it
, and z_it
.
06:37 You probably want to add a little bit of space here just to make this a bit more readable. This will generate a generator object.
06:48
Let’s just confirm, we know that the type of m
is going to be a map
object, and the type of g
is going to be in this case a generator
object.
06:58
And both of these, as we know, are going to be iterators, and so we can iterate over them as we would—say if we needed them in a for
loop or if we wanted to generate a list out of them, we can go ahead and call the list()
function. You know, but here what you could do is just sort of compare what you prefer if you had to write some code that had to do something like this.
07:19
If we were using the generator expression, it’s a little bit more explicit. We are first zipping up the three iterables, or the three lists, into an iterator that will return, one at a time, an iterable that we unpack with x
, y
, and z
, and those are being fed into the sum_of_cubes()
function.
07:39
Whereas with the map()
call, we just simply use the function that we want to map over the elements obtained by zipping, sort of in the background, coming from the three iterables that we’re passing in.
07:54
So, let’s just go ahead and view those, so let’s list(m)
and list(g)
, and those are going to be two lists of the same values.
08:04
Now, another way you can write the generator expression—if I bring that up again—is instead of doing the unpacking over here, we can do the unpacking with the unpacking operator and feed that into the sum_of_cubes()
function.
08:21
So here we’ll write *xyz
and that’ll generate the same list as before, except that the unpacking is being done inside the call to the sum_of_cubes()
function.
08:35
This ends this quick lesson on comparing calls to the map()
function versus, say, generator expressions. Ultimately, you’re going to be the one that decides whether it makes more sense maybe to use a map()
call or a list comprehension, or maybe even a generator expression. But of course, one thing to keep in mind is if you’re writing code that you think others may want to take a look at or may want to contribute to, then list comprehensions and generator expressions appear quite a bit in Python code, and so you may want to choose those instead.
Become a Member to join the conversation.