282

Here is the code I was trying to turn into a list comprehension:

table = ''
for index in xrange(256):
    if index in ords_to_keep:
        table += chr(index)
    else:
        table += replace_with

Is there a way to add the else statement to this comprehension?

table = ''.join(chr(index) for index in xrange(15) if index in ords_to_keep)
martineau
  • 119,623
  • 25
  • 170
  • 301
Josh
  • 4,427
  • 5
  • 28
  • 27

6 Answers6

438

The syntax a if b else c is a ternary operator in Python that evaluates to a if the condition b is true - otherwise, it evaluates to c. It can be used in comprehension statements:

>>> [a if a else 2 for a in [0,1,0,3]]
[2, 1, 2, 3]

So for your example,

table = ''.join(chr(index) if index in ords_to_keep else replace_with
                for index in xrange(15))
Amber
  • 507,862
  • 82
  • 626
  • 550
  • 11
    Note that this only works in Python 2.5 and later. – Kevin Horn Jun 01 '10 at 22:20
  • 8
    Also note, that the `else` is necessary and cannot be ommited, because a resulting value is always required. – sebix Aug 05 '14 at 08:51
  • 2
    The code within join(), is that list comprehension when there is no bracket? Or is that a generator expression and join takes that as argument just fine? – huggie Dec 15 '14 at 07:55
  • 4
    @huggie it's a generator expression, and `join` happily takes a generator or any other iterable. – Amber Dec 15 '14 at 08:41
  • Docs: https://docs.python.org/3/tutorial/datastructures.html#list-comprehensions and https://docs.python.org/3/reference/expressions.html#conditional-expressions – handle Aug 18 '16 at 09:01
  • `[a if a else 2 for a in [0,1,0,3]]` but `[a for a in [0,1,0,3] if a]`. `[a for a if a in [0,1,0,3]]` is a syntax error. List comprehensions are great, but the order sometimes can be confusing and inconsistent. – Eric Duminil May 02 '17 at 20:40
  • @EricDuminil It is not confusing. Instead, your `for` is not complete. And `if a in [0,1,0,3]` would be interpreted as expected but different from what you intended. `[a for a in [0,1,0,3] if a in [1,3]]` would be closer. – unndreay Mar 18 '21 at 12:03
  • @unndreay I wrote this comment a long time ago, at the beginning of my Python journey. What I found confusing is that `if` is left of `for` in one case (with `else`) and right of it with `else`. So I guess my proposal should have been `[a if a for a in [0,1,0,3]]`. It might not be very readable, though. – Eric Duminil Mar 18 '21 at 15:06
  • @EricDuminil No offense :-), I just wanted to clarify a bit in case someone get's lost. – unndreay Mar 19 '21 at 16:18
24

To use the else in list comprehensions in python programming you can try out the below snippet. This would resolve your problem, the snippet is tested on python 2.7 and python 3.5.

obj = ["Even" if i%2==0 else "Odd" for i in range(10)]
Chitrank Dixit
  • 3,961
  • 4
  • 39
  • 59
18

If you want an else you don't want to filter the list comprehension, you want it to iterate over every value. You can use true-value if cond else false-value as the statement instead, and remove the filter from the end:

table = ''.join(chr(index) if index in ords_to_keep else replace_with for index in xrange(15))
Michael Mrozek
  • 169,610
  • 28
  • 168
  • 175
17

Yes, else can be used in Python inside a list comprehension with a Conditional Expression ("ternary operator"):

>>> [("A" if b=="e" else "c") for b in "comprehension"]
['c', 'c', 'c', 'c', 'c', 'A', 'c', 'A', 'c', 'c', 'c', 'c', 'c']

Here, the parentheses "()" are just to emphasize the conditional expression, they are not necessarily required (Operator precedence).

Additionaly, several expressions can be nested, resulting in more elses and harder to read code:

>>> ["A" if b=="e" else "d" if True else "x" for b in "comprehension"]
['d', 'd', 'd', 'd', 'd', 'A', 'd', 'A', 'd', 'd', 'd', 'd', 'd']
>>>

On a related note, a comprehension can also contain its own if condition(s) at the end:

>>> ["A" if b=="e" else "c" for b in "comprehension" if False]
[]
>>> ["A" if b=="e" else "c" for b in "comprehension" if "comprehension".index(b)%2]
['c', 'c', 'A', 'A', 'c', 'c']

Conditions? Yes, multiple ifs are possible, and actually multiple fors, too:

>>> [i for i in range(3) for _ in range(3)]
[0, 0, 0, 1, 1, 1, 2, 2, 2]
>>> [i for i in range(3) if i for _ in range(3) if _ if True if True]
[1, 1, 2, 2]

(The single underscore _ is a valid variable name (identifier) in Python, used here just to show it's not actually used. It has a special meaning in interactive mode)

Using this for an additional conditional expression is possible, but of no real use:

>>> [i for i in range(3)]
[0, 1, 2]
>>> [i for i in range(3) if i]
[1, 2]
>>> [i for i in range(3) if (True if i else False)]
[1, 2]

Comprehensions can also be nested to create "multi-dimensional" lists ("arrays"):

>>> [[i for j in range(i)] for i in range(3)]
[[], [1], [2, 2]]

Last but not least, a comprehension is not limited to creating a list, i.e. else and if can also be used the same way in a set comprehension:

>>> {i for i in "set comprehension"}
{'o', 'p', 'm', 'n', 'c', 'r', 'i', 't', 'h', 'e', 's', ' '}

and a dictionary comprehension:

>>> {k:v for k,v in [("key","value"), ("dict","comprehension")]}
{'key': 'value', 'dict': 'comprehension'}

The same syntax is also used for Generator Expressions:

>>> for g in ("a" if b else "c" for b in "generator"):
...     print(g, end="")
...
aaaaaaaaa>>>

which can be used to create a tuple (there is no tuple comprehension).


Further reading:
handle
  • 5,859
  • 3
  • 54
  • 82
1

Also, would I be right in concluding that a list comprehension is the most efficient way to do this?

Maybe. List comprehensions are not inherently computationally efficient. It is still running in linear time.

From my personal experience: I have significantly reduced computation time when dealing with large data sets by replacing list comprehensions (specifically nested ones) with for-loop/list-appending type structures you have above. In this application I doubt you will notice a difference.

Donald Miner
  • 38,889
  • 8
  • 95
  • 118
  • 1
    woops, i meant to ask about the join method vs. += on a string. – Josh Jun 01 '10 at 17:54
  • Interesting. This (http://wiki.python.org/moin/PythonSpeed#Takeadvantageofinterpreteroptimizations) says otherwise. – kennytm Jun 01 '10 at 17:58
  • @Josh: in older version of Python, the join() method is vastly superior. Newer versions of the interpreter attempt to optimize the += method, but I'm not sure how well this works. I almost always just use the join() method. – Kevin Horn Jun 01 '10 at 22:27
1

Great answers, but just wanted to mention a gotcha that "pass" keyword will not work in the if/else part of the list-comprehension (as posted in the examples mentioned above).

#works
list1 = [10, 20, 30, 40, 50]
newlist2 = [x if x > 30 else x**2 for x in list1 ]
print(newlist2, type(newlist2))

#but this WONT work
list1 = [10, 20, 30, 40, 50]
newlist2 = [x if x > 30 else pass for x in list1 ]
print(newlist2, type(newlist2))

This is tried and tested on python 3.4. Error is as below:

newlist2 = [x if x > 30 else pass for x in list1 ]                                    
SyntaxError: invalid syntax

So, try to avoid pass-es in list comprehensions

Plankton
  • 388
  • 3
  • 13
  • 3
    The if/else construct is just an expression as far as the list comprehension is concerned. If you want to leave items out, you need to put your expression in place of `expr2` here `[expr1 for x in list1 if expr2]` – John La Rooy Mar 21 '17 at 00:08
  • Well pass works in a normal if else block , to me, that implies that it would work everywhere else too. But not so in list comprehensions. – Plankton Mar 21 '17 at 03:04
  • 2
    But this isn't a normal if/else block. It's [an expression](https://docs.python.org/3/faq/programming.html#is-there-an-equivalent-of-c-s-ternary-operator). You can't use elif in there either. – John La Rooy Mar 21 '17 at 20:06