1717

How do I convert the following for-loop containing an if/else into a list comprehension?

results = []
for x in xs:
    results.append(f(x) if x is not None else '')

It should yield '' if x is None, and otherwise f(x). I tried:

[f(x) for x in xs if x is not None else '']

but it gives a SyntaxError. What is the correct syntax?


See Does Python have a ternary conditional operator? for info on ... if ... else ....
See List comprehension with condition for omitting values based on a condition: [... for x in xs if x cond].
See `elif` in list comprehension conditionals for elif.

1
  • The way the question is written, I'd argue that the correct answer would be [f(x if x is not None else '') for x in xs]. Commented Dec 23, 2022 at 12:59

13 Answers 13

2875

You can totally do that. It's just an ordering issue:

[f(x) if x is not None else '' for x in xs]

In general,

[f(x) if condition else g(x) for x in sequence]

And, for list comprehensions with if conditions only,

[f(x) for x in sequence if condition]

Note that this actually uses a different language construct, a conditional expression, which itself is not part of the comprehension syntax, while the if after the for…in is part of list comprehensions and used to filter elements from the source iterable.


Conditional expressions can be used in all kinds of situations where you want to choose between two expression values based on some condition. This does the same as the ternary operator ?: that exists in other languages. For example:

value = 123
print(value, 'is', 'even' if value % 2 == 0 else 'odd')
16
  • 12
    That's why I prefer to put the ternary operator in brackets, it makes it clearer that it's just a normal expression, not a comprehension. Commented Nov 23, 2010 at 20:16
  • 29
    So the trick is "In list compression I write if before for then I have to add else part too". because if my l = [ 2, 3, 4, 5] then [x if x % 2 == 0 for x in l] give me error whereas [x if x % 2 == 0 else 200 for x in l] works. Yes I know to filter it I should write [ x for x in l if x % 2 == 0]. Sorry for botheration. Thanks for your answer. Commented Sep 29, 2013 at 15:29
  • 8
    The python docs mention the ternary operator. Note that it requires the else, or it doesn't work.
    – naught101
    Commented Nov 7, 2013 at 23:23
  • 5
    An example: [x for x in range(50) if (x%3)==0] will return a list of integers divisible by 3. [x if (x%3)==0 for x in range(50)] is invalid, as x if (x%3)==0 is not a valid expression. @Grijesh, here is a counter-example to your rule (if before/after for): [x for x in range(50) if ((x%3)==0 if x>20 else False)]. This comprehension's filter criterion will only match integers that are both divisible by three and greater than 20.
    – sleblanc
    Commented Oct 28, 2014 at 3:44
  • 7
    @Drewdin List comprehensions don’t support breaking during its iteration. You will have to use a normal loop then.
    – poke
    Commented Mar 16, 2015 at 22:33
136

Let's use this question to review some concepts. I think it's good to first see the fundamentals so you can extrapolate to different cases.

Other answers provide the specific answer to your question. I'll first give some general context and then I'll answer the question.

Fundamentals

if/else statements in list comprehensions involve two things:

  • List comprehensions
  • Conditional expressions (Ternary operators)

1. List comprehensions

They provide a concise way to create lists.

Its structure consists of: "brackets containing an expression followed by a for clause, then zero or more for or if clauses".

Case 1

Here we have no condition. Each item from the iterable is added to new_list.

new_list = [expression for item in iterable]
new_list = [x for x in range(1, 10)]
> [1, 2, 3, 4, 5, 6, 7, 8, 9]

Case 2

Here we have one condition.

Example 1

Condition: only even numbers will be added to new_list.

new_list = [expression for item in iterable if condition == True]
new_list = [x for x in range(1, 10) if x % 2 == 0]
> [2, 4, 6, 8]

Example 2

Condition: only even numbers that are multiple of 3 will be added to new_list.

new_list = [expression for item in iterable if condition == True]
new_list = [x for x in range(1, 10) if x % 2 == 0 if x % 3 == 0]
> [6]

But howcome we have one condition if we use two if in new_list?

The prior expression could be written as:

new_list = [x for x in range(1, 10) if x % 2 and x % 3 == 0]
> [6]

We only use one if statement.

This is like doing:

new_list = []
for x in range(1, 10):
    if x % 2 == 0 and x % 3 == 0:
        new_list.append(x)
> [6]

Example 3

Just for the sake of argument, you can also use or.

Condition: even numbers or numbers multiple of 3 will be added to new_list.

new_list = [x for x in range(1, 10) if x % 2 == 0 or x % 3 == 0]
> [2, 3, 4, 6, 8, 9]

Case 3

More than one condition:

Here we need the help of conditional expressions (Ternary operators).

2.Conditional Expressions

What are conditional expressions? What the name says: a Python expression that has some condition.

<Exp1> if condition else <Exp2>

First the condition is evaluated. If condition is True, then <Exp1> is evaluated and returned. If condition is False, then <Exp2> is evaluated and returned.

A conditional expression with more than one condition:

<Exp1> if condition else <Exp2> if condition else <Exp3>...    

An example from Real Python:

age = 12
s = 'minor' if age < 21 else 'adult'
> minor

The value of s is conditioned to age value.

3.List Comprehensions with Conditionals

We put list comprehensions and conditionals together like this.

new_list = [<Conditional Expression> for <item> in <iterable>]

new_list = [<Exp1> if condition else <Exp2> if condition else <Exp3> for <item> in <iterable>]

Condition: even numbers will be added as 'even', the number three will be added as 'number three' and the rest will be added as 'odd'.

new_list = ['even' if x % 2 == 0 else 'number three' if x == 3 else 'odd' 
             for x in range(1, 10)]
> ['odd', 'even', 'number three', 'even', 'odd', 'even', 'odd', 'even', 'odd']

The answer to the question

[f(x) for x in xs if x is not None else '']

Here we have a problem with the structure of the list: for x in xs should be at the end of the expression.

Correct way:

[f(x) if x is not None else '' for x in xs]

Further reading:

Does Python have a ternary conditional operator?

3
  • 4
    By far this is the best answer I can find here and elsewhere. really thank you Mr. Guzman Ojero
    – Dr Neo
    Commented Mar 24, 2022 at 12:31
  • What's going on here ['even' if x % 2 == 0 else 'number three' if x == 3 else 'odd' for x in range(1, 10)]. Is that a nested ternary?
    – Jwan622
    Commented Nov 3, 2023 at 18:22
  • The section 'Case 3: more than one condition' confuses the very issue that this answer otherwise does well to clarify, namely the difference between the condition built into the comprehension loop which acts as a filter, and the condition of a ternary expression which the comprehension loop uses as a map.
    – mmw
    Commented Jan 7 at 11:41
85

The specific problem has already been solved in previous answers, so I will address the general idea of using conditionals inside list comprehensions.

Here is an example that shows how conditionals can be written inside a list comprehension:

X = [1.5, 2.3, 4.4, 5.4, 'n', 1.5, 5.1, 'a']     # Original list

# Extract non-strings from X to new list
X_non_str = [el for el in X if not isinstance(el, str)]  # When using only 'if', put 'for' in the beginning

# Change all strings in X to 'b', preserve everything else as is
X_str_changed = ['b' if isinstance(el, str) else el for el in X]  # When using 'if' and 'else', put 'for' in the end

Note that in the first list comprehension for X_non_str, the order is:

expression for item in iterable if condition

and in the last list comprehension for X_str_changed, the order is:

expression1 if condition else expression2 for item in iterable

I always find it hard to remember that expression1 has to be before if and expression2 has to be after else. My head wants both to be either before or after.

I guess it is designed like that because it resembles normal language, e.g. "I want to stay inside if it rains, else I want to go outside"

In plain English the two types of list comprehensions mentioned above could be stated as:

With only if:

extract_apple for apple in apple_box if apple_is_ripe

and with if/else

mark_apple if apple_is_ripe else leave_it_unmarked for apple in apple_box

1
  • 2
    You can have both. For example, this Buzzless Fizzbuzz: vals = list(range(40)); [val if val % 3 else "Fizz" for val in vals if val % 5] combines both an expression for what goes in the resulting list, as well as a filter condition.
    – Aaron D
    Commented May 25, 2022 at 10:24
53

One way:

def change(x):
    if x is None:
        return f(x)
    else:
        return ''

result = [change(x) for x in xs]

Although then you have:

result = map(change, xs)

Or you can use a lambda inline.

1
  • 14
    This is also a good (maybe only) technique to use when you have to handle possible exceptions from the if expression or code in its or the elses statement block. The accepted answer is better for simple cases.
    – martineau
    Commented Nov 23, 2010 at 21:05
47

Here is another illustrative example:

>>> print(", ".join(["ha" if i else "Ha" for i in range(3)]) + "!")
Ha, ha, ha!

It exploits the fact that if i evaluates to False for 0 and to True for all other values generated by the function range(). Therefore the list comprehension evaluates as follows:

>>> ["ha" if i else "Ha" for i in range(3)]
['Ha', 'ha', 'ha']
0
27
[f(x) if x != None else '' for x in xs]

Syntax for list comprehension:

[item if condition else item for item in items]
[f(item) if condition else value for item in items]
[item if condition for item in items]
[value if condition else value1 if condition1 else value2]
2
  • 1
    This seems like a duplicate of the top answer, with a bit of clarification about list comprehensions. Commented Aug 26, 2020 at 12:17
  • Seems like you forgot the 'for' [value if condition else value1 if condition1 else value2]
    – Tirbo06
    Commented Jan 29, 2022 at 17:13
11

The other solutions are great for a single if / else construct. However, ternary statements within list comprehensions are arguably difficult to read.

Using a function aids readability, but such a solution is difficult to extend or adapt in a workflow where the mapping is an input. A dictionary can alleviate these concerns:

xs = [None, 'This', 'is', 'a', 'filler', 'test', 'string', None]

d = {None: '', 'filler': 'manipulated'}

res = [d.get(x, x) for x in xs]

print(res)

['', 'This', 'is', 'a', 'manipulated', 'test', 'string', '']
0
8

It has to do with how the list comprehension is performed.

Keep in mind the following:

[ expression for item in list if conditional ]

Is equivalent to:

for item in list:
    if conditional:
        expression

Where the expression is in a slightly different format (think switching the subject and verb order in a sentence).

Therefore, your code [x+1 for x in l if x >= 45] does this:

for x in l:
    if x >= 45:
        x+1

However, this code [x+1 if x >= 45 else x+5 for x in l] does this (after rearranging the expression):

for x in l:
    if x>=45: x+1
    else: x+5
3

Make a list from items in an iterable

It seems best to first generalize all the possible forms rather than giving specific answers to questions. Otherwise, the reader won't know how the answer was determined. Here are a few generalized forms I thought up before I got a headache trying to decide if a final else' clause could be used in the last form.

[expression1(item)                                        for item in iterable]

[expression1(item) if conditional1                        for item in iterable]

[expression1(item) if conditional1 else expression2(item) for item in iterable]

[expression1(item) if conditional1 else expression2(item) for item in iterable if conditional2]

The value of item doesn't need to be used in any of the conditional clauses. A conditional3 can be used as a switch to either add or not add a value to the output list.

For example, to create a new list that eliminates empty strings or whitespace strings from the original list of strings:

newlist = [s for s in firstlist if s.strip()]
1
  • 1
    The second one gives an error as Tim answered in his comment, see also the conditional statements in the python docs. Which are quite unreadable to me. Summary: only this if condition else that or a normal expression is allowed. Not value = this if condition (which can be achieved with value = this if condition else None)
    – anderium
    Commented Nov 28, 2019 at 9:03
2

The other answers explain it well but I just wanted to add a bit more about which conditional expression to use in a list comprehension.

As the docs state, list comprehensions are used to create a list using a for-loop and has the general structure:

[expression for item in iterable (0 or more if/for clauses)]

It can create either:

  1. a list by evaluating expression in iteration, or
  2. a subsequence from iterable if an if statement follows the initial for statement

From the above general structure, we can see that else statement cannot follow a for statement; for can follow an if though; so the following is valid code:

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

So any if-else control flow must be done in the expression evaluated above. Also since list comprehensions create a list, expression must be an expression that can be assigned to a variable. So conditional expressions such as a bare if cannot work because we don't know what the value should be if the condition is not True. It is similar to how defining variable x as x = 10 if True is a SyntaxError. So if expression includes a conditional statement, it must have else in it, similar to x = 10 if True else 5 is OK to define variable x.

To sum up, using if/else should be treated as special case of case (1) above where expression is evaluated in iteration, with the caveat that expression includes a condition, like the following:

[(expression1 if condition else expression2) for item in iterable]

  • List comprehension where an if statement follows a for statement (case (2) above) is similar to the math notation to define a subset from a given set, so [x for x in iterable if m<x<n] is similar to {x ∈ iterable | m<x<n}.
  • Since a list comprehension creates a list, it shouldn't be used if creating a list is not the goal; it shouldn't be used simply to write a one-line for-loop; so refrain from writing [print(x) for x in range(5)] for example.
0

There isn't any need for ternary if/then/else. In my opinion your question calls for this answer:

row = [unicode((x or '').strip()) for x in row]
0

You can combine conditional logic in a comprehension:

 ps = PorterStemmer()
 stop_words_english = stopwords.words('english')
 best = sorted(word_scores.items(), key=lambda x: x[1], reverse=True)[:10000]
 bestwords = set([w for w, s in best])


 def best_word_feats(words):
   return dict([(word, True) for word in words if word in bestwords])

 # with stemmer
 def best_word_feats_stem(words):
   return dict([(ps.stem(word), True) for word in words if word in bestwords])

 # with stemmer and not stopwords
 def best_word_feats_stem_stop(words):
   return dict([(ps.stem(word), True) for word in words if word in bestwords and word not in stop_words_english])
-3
# coding=utf-8

def my_function_get_list():
    my_list = [0, 1, 2, 3, 4, 5]

    # You may use map() to convert each item in the list to a string, 
    # and then join them to print my_list

    print("Affichage de my_list [{0}]".format(', '.join(map(str, my_list))))

    return my_list


my_result_list = [
   (
       number_in_my_list + 4,  # Condition is False : append number_in_my_list + 4 in my_result_list
       number_in_my_list * 2  # Condition is True : append number_in_my_list * 2 in my_result_list
   )

   [number_in_my_list % 2 == 0]  # [Condition] If the number in my list is even

   for number_in_my_list in my_function_get_list()  # For each number in my list
]

print("Affichage de my_result_list [{0}]".format(', '.join(map(str, my_result_list))))

(venv) $ python list_comp.py
Affichage de my_list [0, 1, 2, 3, 4, 5]
Affichage de my_result_list [0, 5, 4, 7, 8, 9]

So, for you: row = [('', unicode(x.strip()))[x is not None] for x in row]

2
  • What does "Affichage de..." mean? Is it French? Commented Apr 22, 2020 at 14:11
  • @PeterMortensen French indeed, means "Displaying / overview of". So Displaying / overview of my_result_list
    – Nomad
    Commented Jun 10, 2020 at 10:44

Not the answer you're looking for? Browse other questions tagged or ask your own question.