# Comprehension with two variables - explanation needed

Chris Angelico rosuav at gmail.com
Sun Nov 23 16:21:35 CET 2014

```On Mon, Nov 24, 2014 at 1:56 AM, Ivan Evstegneev
<webmailgroups at gmail.com> wrote:
>
> Hello guys,
>
> I would like to ask you for some explanations on comprehensions. (Don’t be scared, it just some particular example ^_^)
>
> I found this little “find prime number” example over the internet:
>
> >>> noprimes = [j for i in range(2, 8) for j in range(i*2, 50, i)]
> >>> primes = [x for x in range(2, 50) if x not in noprimes]
> >>> print primes
>
> [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47]

Okay, this is a pretty ham-fisted way of enumerating prime numbers,
but let me walk you through it. First, let's look at the composite
numbers (the 'noprimes' list). That comprehension is identical (apart
from some details that don't matter here) to this:

noprimes = []
for i in range(2,8):
for j in range(i*2, 50, i):
noprimes.append(j)

So, as you correctly deduced below, what you have is this list:
[4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38,
40, 42, 44, 46, 48, 6, 9, 12, 15, 18, 21, 24, 27, 30, 33, 36, 39, 42,
45, 48, 8, 12, 16, 20, 24, 28, 32, 36, 40, 44, 48, 10, 15, 20, 25, 30,
35, 40, 45, 12, 18, 24, 30, 36, 42, 48, 14, 21, 28, 35, 42, 49]

The inner loop (with j as its iteration variable) counts up in
increments of i, starting at twice i, stopping once it's gone past
fifty. That means it's every multiple of i other than i itself, which
means these are by definition composite numbers. Note that quite a few
of the numbers are coming up more than once; this would be more
efficient as a set comprehension, but leave that aside for now.

Note also that the range for i has to go as far as the square root of
the number we're working with - we have to go as far as 7*7==49. If
you want primes to some higher cap, say 100, you'd need to also
increase that, eg range(2,11).

> The change that I tried to apply to this comprehension would lead to the following pattern: noprimes = = [[4, 6,8…48], [6, 9, 12….], ….,[some numbers]]
>
> But despite my struggling on it for the whole day, I haven’t got to the desirable result(using comprehensions only)
>
> Please, don’t get me wrong, I’m not a lazy one (just a beginner).
>
> In my case, I  made some googling, and found some patterns for matrices, that look this one(for example):
>
> Matrix = [[0 for x in range(5)] for x in range(5)]

The inner lists there can be created more simply, since you're working
with integers:

[[0]*5 for x in range(5)]

> But when I tried to use square brackets with my code, it yields an error about definition of “i”, for instance:
>
> File "<pyshell#30>", line 1, in <module>
>
> noprimes = [[j for i in range(2, 8)] for j in range(i*2, 50, i)]
>
> NameError: name 'i' is not defined.

for loops running left-to-right, they now are explicitly bracketed to
be the other way around. This is more like:

noprimes = []
for j in range(i*2, 50, i):
noprimes.append([j for i in range(2, 8)])

which can be unrolled another level:

noprimes = []
for j in range(i*2, 50, i):
tmp = []
for i in range(2, 8):
tmp.append(j)
noprimes.append(tmp)

Now you can see why i doesn't exist there.

> So I moved further, and succeeded to solve my problem in a different manner:
> >>> noprimes=[]
>
> >>> for i in range(2,8):
>
> noprimes.append(list(range(i*2,50,i)))
>
> Which yields, in fact, the matrix I wanted from the beginning:
>
> Yep, this the “classical” way for me, on the other hand I have the feeling, that this is not, the ultimate solution. ^_^

In fact, this is exactly right, and now you can easily translate that
back to list comp syntax:

noprimes = [list(range(i*2,50,i)) for i in range(2,8)]

> So my questions is still persists:
>
> 1)      Is it possible to realize such a thing by applying changes only to initial comprehension? If yes, it would be nice to see the solution.
> 2)      How actually bad is my solution? I mean, after all, there are nested functions call in my code,  so how much the speed is affected(if any)?

Don't worry about speed of function calls. The algorithm used here is
so utterly appalling that it's going to get in your way long before
the performance of Python will. :)

List comprehensions are fairly readily convertible to and from
explicit loops. There are a couple of minor differences (a list comp
is actually executed inside a nested function, so the loop iterator
variable can't leak out), but generally, if you're having trouble,
just unroll the comprehension into a loop and see if it makes more
sense.

ChrisA

```