Python: List comprehension vs Generator expression

Laksh Samdariya
GDSC GHRCE
Published in
7 min readOct 16, 2021

Python! We have heard this name a lot since this pandemic. From small children to us (developers), all have been learning and developing software and programs using it. So, is python so easy?

It might be easy compared to other programming languages like C/C++/Java/Ruby etc. but, it’s not as easy as accessing Spotify or Instagram (developed using Python). It has its fair share of challenges and difficulties however this doesn’t mean you can’t learn it. Learning python is an art and I am your artist here, I’m going to show you a small hack to make sure your program runs smoothly using List comprehensions or Generator expressions.

So when I started studying Python, the first thing that I came across was FOR loops, used a lot. At first, I found them to be complicated and a bit difficult (as a beginner). Another thing I did not like was WHILE loops, which made it even more confusing to me. I was told that what a FOR loop can do, the same can be done by WHILE loop but vice versa is not possible. An immediate question popped up in my head… Bhai agar WHILE loop FOR loop ka kaam karra hai plus additional kaam kar raha hai why not just use it? Well, the answer is simple, you need to get through a lot of complications to make a WHILE loop work and it occupies a lot more space than FOR (space in the sense.. Increases the length of the code. Codes are supposed to be shorter since for programmers shorter is sweeter). So, I got through it and learned how to use FOR loops.

After this, I came across LIST. Everything was going well until I started learning List comprehensions. It just blew my mind. It was confusing because of the multiple ways to construct a List comprehension using conditions before and after and soo much more. Now, this started to be a headache for me. (PROGRAMMING IS NEVER EASY). I Struggled a lot to understand it and finally did.

So now, let’s get out of my story, and let’s see what LIST COMPREHENSIONS are.

LIST COMPREHENSION:

List comprehension gives us a shorter syntax compared to for loop when you want to create a new loop based on the values of some existing loop.

List comprehension is used when we want to create a new list whose values depend upon the values of some existing list in your program.

It has 3 benefits:

  1. It makes your code shorter.
  2. It makes it easy to understand.
  3. Creates a list automatically without you asking it to make one.

Now we’ll see some examples to get a better understanding.

Ex 1: Here, we have used a for loop and the code seems to be big. Writing big programs would be a waste of time as we have to write soo many additional lines.

names = ["vidhanshu","vidit","sobhan","laksh"]empty = []for name in names:    if name[0] == 'v':        empty.append(name)print(empty)<['vidhanshu', 'vidit']>

OR

We have done the same with a single line of code.

names = ["vidhanshu","vidit","sobhan","laksh"][name for name in names if name[0] == 'v']<['vidhanshu', 'vidit']>

Ex 2: Write a program that prints numbers less than 6 when we pass in numbers 1–10.

Hold on… try it out on your own and then refer below.

— — — — — — — — — — — — — — — — — — — — — — — — — — — — — — —

You can solve the above problem by both these methods but the recommended one is list comprehension.

eemp = []for num in range(10):    if num < 6:        eemp.append(num)print(eemp)<[0, 1, 2, 3, 4, 5]>

OR

eemp = [num for num in range(10) if num <6]print(eemp)<[0, 1, 2, 3, 4, 5]>

Using List comprehensions we can even get output as boolean values. This might be used for a few built-in functions or if we need to access boolean values elsewhere in our program.

Like:

emp = [num < 6 for num in range(10)]print(emp)<[True, True, True, True, True, True, False, False, False, False]>names = ["vidhanshu","vidit","sobhan","laksh"]empty = [name[0] == 'v' for name in names]print(empty)<[True, True, False, False]>

Let me introduce you to the two built-in functions where the boolean values from list comprehension can be used.

  1. ALL
  2. ANY

ALL: It is a build-in function that will return True if all the iterables are Truthy. It will return True even if the iterable is empty.

all([num for num in [0,1,2,3]])# 0 is always considered False. Since we have a single False element the whole list becomes False.<False>all([])# As mentioned above, an empty list will return True. (list is iterable)<True>all([num < 6 for num in range(6)])# Range(6) means all the numbers from 0–5. And all these numbers are less than 6, thus True.<True>all([num < 6 for num in range(10)])# Range(10) means all the numbers from 0–9. This even includes numbers greater than 6 which is False, thus False.<False>

I hope ALL function is clear.

ANY: any guesses????

Yes, I guess that many of you have guessed it right!

ANY is again another build-in function that gives an output True if “any-one” of the elements of the list is Truthy. Here it returns False when we send an empty iterable.

any([num for num in [0,1,2,3]])# Here 0 is falsy but doesn't matter, 1 is the truth so it'll return True only.<True>any([])# As mentioned above, empty iterable will be False.<False>any([num < 6 for num in range(10)])# range(10) is from 0–9. Up to 5 It's True, hence the output will be True.<True>
any([num % 2 == 0 for num in [10,2,16,18,21]])# 10,2,16,18 are divisible by 2 gives remainder 0, hence True (doesn't matter if 21 is divisible or not.)<True>any([num % 2 == 1 for num in [10,2,16,18,21]])# 21, when divided by 2, gives 1 as remainder. Hence True<True>any([num % 2 == 2 for num in [10,2,16,18,21]])# None of those divided by 2 will give the remainder as 2. Hence False<False>

While working on these functions I made a mistake. i.e:

any(num % 2 == 1 for num in [10,2,16,18,21])<True>
any(num % 2 == 2 for num in [10,2,16,18,21])<False>
all(num < 6 for num in range(10))<False>
names = ["vidhanshu","vidit","sobhan","laksh"]all(name[0] == 'v' for name in names)<False>

In all these examples, I removed them from the list and placed them between parentheses of ALL and ANY.

I observed no change in the result. BUT BUT BUT… the change was in the method or the syntax used.

Printing just the parentheses and the matter inside it got the output to be:

names = ["vidhanshu","vidit","sobhan","laksh"](name[0] == 'v' for name in names)<generator object <genexpr> at 0x7f6548d2bbd0>

GENERATOR OBJECT. Yes, this is another type of syntax used. It is called the GENERATOR.

GENERATOR EXPRESSION

Generators are similar to functions, they use “yield()” in place of “return()” to return a result. It is more powerful as a tool to implement iterators.

Various other generator expressions can be coded similar to List comprehensions, but instead of square brackets, we use parentheses.

generator = (num ** 3 for num in range(5))for num in generator:    print (num)<0 
1
8
27
64>
generator = ([num ** 3 for num in range(5)])print(generator)<[0, 1, 8, 27, 64]>
generator = (num ** 3 for num in range(5))print(list(generator))<[0, 1, 8, 27, 64]>

The only difference between these two is that we are converting the var “generator” into a list while printing in the second and we are using a list comprehension, which by default saves it into a list.

So what is the difference between List comprehension and Generator expression?

There are two differences:

  1. Time
  2. Memory efficiency

List comprehensions are faster while Generators are more memory efficient.

%timeit listcomp = sys.getsizeof([x*10 for x in range(500)])%timeit generatorexp = sys.getsizeof(x*10 for x in range(500))<10000 loops, best of 5: 38.6 µs per loop 
The slowest run took 6.25 times longer than the fastest. This could mean that an intermediate result is being cached.
1000000 loops, best of 5: 771 ns per loop>
import syslistcomp = sys.getsizeof([x*10 for x in range(500)])generatorexp = sys.getsizeof(x*10 for x in range(500))print(f"List comprehension takes {listcomp} bytes")print(f"Generator expression takes {generatorexp} bytes")<List comprehension takes 4280 bytes
Generator expression takes 128 bytes>

Use a Generator if all you are doing is iterating only once. If you want to store and use the generated result only then go with List comprehensions.

It doesn’t matter what you choose. Both have pros and cons. You only need to worry when your code runs slow. Only then you have to choose carefully that whether you should go with Generators or List comprehensions.

Using Generators, you can print or store the value in any iterable format (dictionaries, tuples, or list), whereas in list comprehension it’s default stored as a list.

For some parts of python, you’ll find it to be a little confusing, but as you move forward, you will feel like the previous part was easy. This is because python is an easy-to-do language. But, nothing is always easy, it all starts with complications and then becomes easy.

Chill and don’t worry. Python will be your piece of cake.

REFERENCES

  1. https://stackoverflow.com/questions/47789/generator-expressions-vs-list-comprehensions
  2. https://www.geeksforgeeks.org/generator-expressions/

--

--