Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Inconsistent comprehension syntax?

I just stumbled over what seems to be a flaw in the python syntax-- or else I'm missing something.

See this:

[x for x in range(30) if x % 2 == 0]

But this is a syntax error:

[x for x in range(30) if x % 2 == 0 else 5]

If you have an else clause, you have to write:

[x if x % 2 == 0 else 5 for x in range (30)]

But this is a syntax error:

[x if x %2 == 0 for x in range(30)]

What am I missing? Why is this so inconsistent?

like image 878
temporary_user_name Avatar asked Nov 16 '13 23:11

temporary_user_name


2 Answers

You are mixing syntax here. There are two different concepts at play here:

  • List comprehension syntax. Here if acts as a filter; include a value in the iteration or not. There is no else, as that is the 'don't include' case already.

  • A conditional expression. This must always return a value, either the outcome of the 'true' or the 'false' expression.

Remember: list comprehensions produce a sequence of values from a loop. By using if you can control how many elements from the input iterable are used for the output.

A conditional expression on the other hand, works like any other expression: an expression always produces a single result; the conditional expression lets you pick between two possible outcomes. But because it must produce a result you can’t write one without the else part.

Note that expressions can and are frequently nested. The conditional expression itself contains three sub-expressions:

expr1 if expr2 else expr3
# ^                   ^
# \ used when expr2   |
#   is true           |
#                     /
#   used when expr2 is
#   false

List comprehension also contain sub-expressions. The one at the start (before the for <target> in <iterable> part) is the sub-expression that is executed each iteration to produce the value in the output list. The iterator expression (after in) is another. The optional if filter takes an expression too. In any of these places you could use a conditional expression if you so choose. But, that doesn’t mean you can add an extra else to the list comprehension without the other parts of the conditional expression syntax.

like image 191
Martijn Pieters Avatar answered Nov 05 '22 18:11

Martijn Pieters


The difference between the two is that the trailing if in the first one is part of the list comprehension syntax, while the if-else is the conditional operator, not any part of the list comprehension syntax - as it is an expression which is permitted in that part of a list comprehension.

The syntax for the conditional operator is as follows:

x if condition1 else y

This returns the value of the expression that is evaluated, which is why it seems to "work" for your case, though it evaluates all of the time and returns always - which is the key difference between the two.

Meanwhile, for the list comprehension, it tests whether or not the condition applies, and does not add that in the new list created if the condition does not evaluate to true according to the Truth Value Testing procedure, not None nor anything else.

Compare the following (taking an example from PEP202):

a = [i if i % 2 == 0 else None for i in range(20)]
b =  [i for i in range(20) if i % 2 == 0]

a would be

[0, None, 2, None, 4, None, 6, None, 8, None, 10, None, 12, None, 14, None, 16, None, 18, None]

while b would be

[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]

which isn't the same at all, as no matter what the result of it is, it will still add it in if there is no if part of the list comprehension.

like image 6
Qantas 94 Heavy Avatar answered Nov 05 '22 18:11

Qantas 94 Heavy