Comprehensions build a collection in one line; the iterator protocol is the machinery every for loop runs on.
Comprehensions
- List:
[expr for x in items if cond]— expression first, thenfor, then optionaliffilter. - Dict:
{k: v for x in items}— produces key/value pairs. - Set:
{expr for x in items}— drops duplicates automatically. - Filter skips items that fail
cond; it does not transform them. - Cost: O(n) time, O(n) space. Same work as an explicit loop, just more compact.
When to use one
- Right tool when short and readable, like one sentence: one transform, one filter.
- Wrong tool for nested conditions, multiple steps,
print, or error handling. - Statements (
if/elseblocks,try, multiple steps) are not allowed inside anyway — use a loop.
Iterable vs iterator
- Iterable: anything you can
for-loop over (list, string, dict, set, range, file). Can hand out items one at a time; they need not all be in memory. - Iterator: the moving cursor that remembers its position and yields the next item.
iter(x)returns a fresh iterator at the start;next(it)returns the next item and advances.StopIteration: the exceptionnextraises when items run out; theforloop catches it.
For loop under the hood
it = iter(items) # once
loop:
x = next(it) # pull next
except StopIteration -> break
run body
repeat
enumerate and zip
enumerate(x)pairs each item with its index (from 0);start=1to offset.zip(a, b, ...)walks several iterables in step, pairing by position.zipstops at the shortest input, dropping extras.- Both are lazy: O(1) extra space, O(n) time for a full loop.
Gotchas
- Iterators are one-shot: once exhausted, they stay empty. Re-iterate the original collection for a fresh cursor.
zipsilently truncates unequal lengths — check first if that matters.- Never modify a list while looping it; build a new list with a comprehension instead.
{}is an empty dict, not a set. Useset()for an empty set.