How do list comprehensions work and when should I use one?
Short answer
A list comprehension builds a list in one expression combining a loop and an optional condition. Use it for simple transform-and-filter tasks, but fall back to a normal loop when the logic gets complicated.
The short version
A list comprehension builds a list in a single expression, combining the value you want, a for loop, and an optional if filter. Use one for a clear transform or filter. When the logic grows several branches or side effects, a plain loop is more readable, and readability wins.
The basic shape
numbers = [1, 2, 3, 4]
squares = [n * n for n in numbers]
print(squares) # [1, 4, 9, 16]
Read it left to right: the result value first (n * n), then where the values come from (for n in numbers).
Adding a filter
An if at the end keeps only the items you want.
evens = [n for n in numbers if n % 2 == 0]
print(evens) # [2, 4]
This is equivalent to a loop with an if and an append, but shorter and, for simple cases, clearer.
The wrong way that people write first
squares = []
for n in numbers:
squares.append(n * n)
Nothing is wrong with this, but for a straight transform the comprehension says the same thing in one line, and the intent (build a list of squares) is visible at a glance.
The if-else placement gotcha
A filtering if goes at the end. A choosing if-else goes at the front, because it is part of the value expression. Mixing these up is a common error.
[n if n > 2 else 0 for n in numbers] # choose per item: [0, 0, 3, 4]
[n for n in numbers if n > 2] # filter items: [3, 4]
When not to use one
If you find yourself nesting three loops, adding a ternary inside a ternary, or wanting to run code that does something other than produce a value, stop and write a normal loop. Comprehensions are for building a collection cleanly, not for cramming logic onto one line. A comprehension that a colleague has to decode slowly is worse than a plain loop they read instantly.
Related forms
The same syntax with braces builds sets and dictionaries, and with parentheses builds a lazy generator that produces items on demand rather than all at once.
{n * n for n in numbers} # set
{n: n * n for n in numbers} # dict
(n * n for n in numbers) # generator