List comprehension


#1
cubes_by_four = [x ** 3 for x in range(1, 11) if ((x ** 3) % 4) == 0]

So I am doing list comprehension and I kinda get it. But can someone explain to me how to read the calculation in the cubes_by_four list?

And why does it give:

[8, 64, 216, 512, 1000]

as answer?


#2

There’s two calculations there, really - are you stuck with one in particular, or is it the whole statement?

If I re-frame things slightly, is it any clearer this way:

You’re doing one calculation (x cubed) for every number in your range, and only keeping the ones which satisfy your if statement, which is doing another calculation on x-cubed.

Does that help explain why you’re seeing that output?


#3

The value written to the returned list will meet this condition (the cube is divisible by 4). Hence, only 8, 64, 216, 512, and 1000 qualify as return values.

The list comprehension above is the near equivalent of a filter.

>>> list(filter(lambda x: x ** 3 % 4 == 0, range(1, 11)))
[2, 4, 6, 8, 10]
>>> 

which result we could map to a new list of cubes…

>>> list(map(lambda x: x ** 3, list(filter(lambda x: x ** 3 % 4 == 0, range(1, 11)))))
[8, 64, 216, 512, 1000]
>>> 

Of course, the list comprehension is so much more readable, and simpler.


#4

Thanks for the reply but I don’t know what filters and lamba is yet. So that seems a step ahead of where I am


#5

Yes thanks, this makes it a bit clearer now. But where does the answer 8 come from? Is it because the first bit of calculation calculates 2^3 which is 8 ? And 8 is divisible by 4?

I’m sorry if im unclear here with my question lol :face_with_raised_eyebrow:


#6

And for now you can ignore it. They will both be introduced in the unit that introduces list comprehensions (Advanced topics) so just pour on.

Short answer, yes.


#7

So it returns the answer rather than the number on which it calculates. So 8 instead of 2.

And one more thing:
2^3 basically means 2 by 2 by 2 right?


#8

Correct. That value is the cube of x as given in the left end of the comprehension. The value is arbitrary.

c = [_arbitrary_value_based_on_x for x in _iterable_ if x _meets_condition]

#9

Awesome I get it… Thanks both!


#10

It does get a little crazier, but ignore this for now…

>>> parity = ['odd' if x % 2 else 'even' for x in range(1,11)]
>>> parity
['odd', 'even', 'odd', 'even', 'odd', 'even', 'odd', 'even', 'odd', 'even']
>>> 

#11

That’s an expression though, not part of comprehension syntax

@ariej filter/map are function versions of list comprehension. That’s to say you don’t need them, list comprehension is preferred (because python’s creator prefers them and doesn’t much like map/filter/reduce/lambda)

There are also set/dict/generator expressions/comprehensions

# (generators are lazy, this hasn't fully evaluated yet)
# <generator object <genexpr> at 0x7f022459bd00>
(n for n in range(10))

# {0: 1, 1: 2, 2: 3, 3: 4, 4: 5, 5: 6, 6: 7, 7: 8, 8: 9, 9: 10}
{n:n+1 for n in range(10)}

# {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
{n for n in range(10)}


#12

Not sure I follow you. How else does one create that list, in simple terms, such as comprehension?


#13

a if b else c
Is a ternary expression
Sure you can put that in list comprehension, it just isn’t part of it
it gets crazier, but it’s not the list comprehension itself that got crazier. It was merely adding more operations in the expression part.

Oh and multiple sources can be used:

[(a, b) for a in range(10) for b in range(3)]
[(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2), (2, 0), (2, 1), (2, 2), (3, 0), (3, 1), (3, 2), (4, 0), (4, 1), (4, 2), (5, 0), (5, 1), (5, 2), (6, 0), (6, 1), (6, 2), (7, 0), (7, 1), (7, 2), (8, 0), (8, 1), (8, 2), (9, 0), (9, 1), (9, 2)]

Which behaves as if they are nested


#14
>>> list('odd' if x % 2 else 'even' for x in range(11))
['even', 'odd', 'even', 'odd', 'even', 'odd', 'even', 'odd', 'even', 'odd', 'even']
>>> 

That permits an a or b assignment to the list.


#15

I mean it’s not any different from addition in that list comprehension doesn’t have any optional syntax for doing addition, but you’re free to use it in the expression describing the result of each value

It is its own independent thing

a = 100
b = 'a is large' if a > 10 else 'a is small'

In languages with c-like syntax it’s generally written as ?:

let a = 100
let b = a > 10 ? 'a is large' : 'a is small'