Skip to content

prefer-collection-comprehension

Single-purpose fresh collection builder loop — prefer a direct comprehension.

Why

An empty collection followed by a loop whose only behavior is one projection or filtered insertion spreads a declarative map/filter across mutable scaffolding.

Fix

Build the fresh collection with one dict, list, or set comprehension. When a list candidate must be computed once and reused in the element, bind it in the filter with :=. Keep the loop when mutation is incremental, evaluation order is observable, or the imperative form carries additional behavior.

Examples

Before — flagged Compute and filter a list candidate directly
app/intervals.py
def clipped(items, bounds):
result: list[Interval] = []
for item in items:
value = item.clipped(bounds)
if value is not None:
result.append(value)
return result
After — preferred Keep a computed filtered list declarative
app/intervals.py
def clipped(items, bounds):
return [value for item in items if (value := item.clipped(bounds)) is not None]
Before — flagged Build a projected dictionary directly
app/capacity.py
def organization_caps(rows):
caps: dict[str, int] = {}
for row in rows:
caps[row.organization_id] = row.organization_cap
return caps
After — preferred Keep the collection projection declarative
app/capacity.py
def organization_caps(rows):
return {row.organization_id: row.organization_cap for row in rows}