Når list comprehensions klikker for deg, vil du aldri mer skrive en for-løkke som legger til i en liste. De er ikke bare syntaktisk sukker — de signalerer hensikt tydelig og kjører raskere enn den tilsvarende løkken i CPython takket være bytecode-optimalisering. Ifølge den offisielle Python-opplæringen, gir comprehensions en kortfattet måte å opprette lister basert på eksisterende sekvenser eller andre iterables. Denne artikkelen bygger den mentale modellen først, og dekker deretter hvert reelt mønster: filtrering, nesting, dict- og set-comprehensions, og det ene tilfellet der du bør bruke en vanlig for-løkke i stedet.

Det grunnleggende mønsteret

Anatomien til en list comprehension er [uttrykk for element i iterable]. Tre deler: utgangsuttrykket (hva hvert element blir), løkkevariabelen og iterabelen å hente fra. Start med en kjent løkke og fold den ned.

python
# Plain for loop — building a list of file sizes in KB
file_sizes_bytes = [1024, 204800, 51200, 3145728, 8192]

sizes_kb = []
for size in file_sizes_bytes:
    sizes_kb.append(size / 1024)

# print(sizes_kb)  →  [1.0, 200.0, 50.0, 3072.0, 8.0]

# Same result as a list comprehension — one line, same meaning
sizes_kb = [size / 1024 for size in file_sizes_bytes]

Comprehensionen leser nesten som norsk: "gi meg size / 1024 for hver size i file_sizes_bytes." Den klarheten er den virkelige gevinsten — en leser trenger ikke å spore et append-kall for å forstå hva du bygger.

python
# Another common pattern: deriving one list from another
usernames = ["alice", "bob", "carol"]

# Build a list of display names
display_names = [name.capitalize() for name in usernames]
# ['Alice', 'Bob', 'Carol']

# Or extract a single field from a list of dicts
users = [
    {"id": 1, "name": "Alice", "role": "admin"},
    {"id": 2, "name": "Bob",   "role": "viewer"},
    {"id": 3, "name": "Carol", "role": "editor"},
]

names = [user["name"] for user in users]
# ['Alice', 'Bob', 'Carol']

Filtrering med if

Legg til en betingelse på slutten, og bare elementer som passerer kommer med i utgangslisten: [uttrykk for element i iterable if betingelse]. Dette er mønsteret som erstatter en for + if + append-kombinasjon.

python
# Loop version — extract only active users
active_users = []
for user in users:
    if user["active"]:
        active_users.append(user["name"])

# Comprehension version — identical result
users = [
    {"name": "Alice", "active": True},
    {"name": "Bob",   "active": False},
    {"name": "Carol", "active": True},
    {"name": "Dave",  "active": False},
]

active_users = [user["name"] for user in users if user["active"]]
# ['Alice', 'Carol']
python
# Filtering a raw CSV row — drop blanks and whitespace-only values
raw_row = ["[email protected]", "", "  ", "editor", " "]

clean_row = [field.strip() for field in raw_row if field.strip()]
# ['[email protected]', 'editor']

# Filtering a list of log levels
log_lines = [
    "INFO  server started",
    "DEBUG loading config",
    "ERROR database timeout",
    "DEBUG query took 450ms",
    "ERROR disk space low",
]

errors = [line for line in log_lines if line.startswith("ERROR")]
# ['ERROR database timeout', 'ERROR disk space low']

Arbeid med strenger

Strengbehandling er der comprehensions virkelig beviser sin verdi. Kombinasjonen av Pythons rike strengmetoder og comprehension-syntaks holder transformasjonspipelines lesbare uten mellomliggende variabler.

python
# Strip whitespace from tags coming out of a form field
raw_tags = ["  python ", "data science", " machine-learning ", "API"]

tags = [tag.strip().lower() for tag in raw_tags]
# ['python', 'data science', 'machine-learning', 'api']

# Normalise email addresses from a signup CSV
raw_emails = ["[email protected]", "  [email protected]  ", "[email protected]"]

emails = [e.strip().lower() for e in raw_emails]
# ['[email protected]', '[email protected]', '[email protected]']

# Extract file extensions from a list of uploaded filenames
filenames = ["report.pdf", "avatar.PNG", "data.CSV", "archive.tar.gz", "notes.txt"]

extensions = [name.rsplit(".", 1)[-1].lower() for name in filenames if "." in name]
# ['pdf', 'png', 'csv', 'gz', 'txt']
Tips: Når du normaliserer brukerinndata, strip før du lowercase — ellers vil en verdi som " [email protected] " bestå en .lower()-sjekk på innholdet, men fortsatt ha ledende mellomrom som vil ødelegge et databaseoppslag eller JSON-nøkkeloppslag.

Dict- og set-comprehensions

Den samme ideen utvides til dicts og sets. En dict-comprehension bruker krøllede parenteser med et nøkkel: verdi-par: {nøkkel: verdi for element i iterable}. En set-comprehension dropper kolonet og produserer en deduplisert samling: {uttrykk for element i iterable}.

python
# Invert a dict — swap keys and values
permissions = {"alice": "admin", "bob": "viewer", "carol": "editor"}

by_role = {role: name for name, role in permissions.items()}
# {'admin': 'alice', 'viewer': 'bob', 'editor': 'carol'}

# Build a fast lookup dict from a list of user records
users = [
    {"id": 101, "name": "Alice", "active": True},
    {"id": 102, "name": "Bob",   "active": False},
    {"id": 103, "name": "Carol", "active": True},
]

# O(1) lookups by ID — much faster than scanning the list every time
user_by_id = {user["id"]: user for user in users}
# {101: {...}, 102: {...}, 103: {...}}

# Access a user directly
user_by_id[102]["name"]  # 'Bob'
python
# Set comprehension — deduplicate a list of file extensions
uploads = ["report.pdf", "data.csv", "summary.pdf", "export.CSV", "notes.txt"]

unique_extensions = {name.rsplit(".", 1)[-1].lower() for name in uploads if "." in name}
# {'pdf', 'csv', 'txt'}  — order not guaranteed

En advarsel med sets: rekkefølge er ikke bevart. Hvis du trenger å deduplisere en liste mens du bevarer den opprinnelige rekkefølgen, er en set-comprehension feil verktøy — bruk list(dict.fromkeys(items)) i stedet, som utnytter den innsettingsordnede atferden til dicts i Python 3.7+.

Nestede comprehensions

Du kan neste comprehensions for å iterere over nestede strukturer. Den vanligste bruken er å flate ut en liste med lister — en matrise fra en CSV-parsing, sideinndelte API-svarssider eller grupperte spørringsresultater.

python
# Flatten a 2D list (e.g. paginated API results)
pages = [
    [{"id": 1, "name": "Alice"}, {"id": 2, "name": "Bob"}],
    [{"id": 3, "name": "Carol"}],
    [{"id": 4, "name": "Dave"}, {"id": 5, "name": "Eve"}],
]

all_users = [user for page in pages for user in page]
# [{'id': 1, ...}, {'id': 2, ...}, {'id': 3, ...}, {'id': 4, ...}, {'id': 5, ...}]

# The order mirrors what nested for loops would produce:
# for page in pages:
#     for user in page:
#         all_users.append(user)

Les nestede comprehensions fra venstre til høyre — den ytre løkken kommer først, den indre løkken andre. Det samsvarer med rekkefølgen til nestede for-løkker.

python
# Two levels — fine and readable
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
flat = [cell for row in matrix for cell in row]
# [1, 2, 3, 4, 5, 6, 7, 8, 9]

# Three levels — stop here. Use a plain loop or a helper function.
# This is the point where readability dies:
cube = [[[1,2],[3,4]],[[5,6],[7,8]]]

# Don't do this:
flat3 = [val for layer in cube for row in layer for val in row]

# Do this instead:
flat3 = []
for layer in cube:
    for row in layer:
        flat3.extend(row)
Generatoruttrykk bruker samme syntaks med parenteser i stedet for hakeparenteser: (user["id"] for user in users). De evaluerer dovent — elementer produseres ett om gangen i stedet for å bygge hele listen i minnet. Bruk dem når du bare trenger å iterere én gang, eller når du sender resultatet direkte til sum(), max(), any() eller lignende funksjoner. Se generatoruttrykksreferansen for de fullstendige detaljene.

if/else-ternær inne i en comprehension

Når du trenger å transformere elementer til én av to verdier — i stedet for å droppe dem — bruk et ternært uttrykk i utgangsposisjonen. Posisjonen er viktig: ternæren hører hjemme i starten, ikke etter iterabelen.

python
# Correct: ternary is the output expression
users = [
    {"name": "Alice", "active": True},
    {"name": "Bob",   "active": False},
    {"name": "Carol", "active": True},
]

status = ["active" if user["active"] else "inactive" for user in users]
# ['active', 'inactive', 'active']

# Normalise a config value — replace None with a default
raw_config = ["/var/log", None, "/tmp", None, "/etc/app"]

paths = [path if path is not None else "/var/log/default" for path in raw_config]
# ['/var/log', '/var/log/default', '/tmp', '/var/log/default', '/etc/app']
python
# Common mistake — putting the ternary after the iterable (SyntaxError)
# status = ["active" for user in users if user["active"] else "inactive"]  # WRONG

# The trailing "if" is a filter — it drops non-matching items entirely.
# The ternary ("if ... else ...") in the output expression transforms all items.
# They serve different purposes. You can combine them:

# Keep only users, but show their status
status = ["active" if user["active"] else "inactive"
          for user in users
          if user["name"] != "Bob"]
# ['active', 'active']  — Bob was filtered out entirely

Når man bruker en for-løkke i stedet

List comprehensions er for å bygge lister. Hvis det du egentlig gjør er å kjøre sideeffekter, bruk en for-løkke — ikke en comprehension. Dette betyr mer enn stil: en comprehension som kaster bort resultatet, sløser minne ved å bygge en liste ingen bruker, og skjuler hensikten for leseren.

python
# Bad — comprehension for side effects (writing to a file, printing, calling an API)
[print(f"Processing user {user['name']}") for user in users]   # don't do this
[requests.post("/api/notify", json=user) for user in users]    # definitely don't do this

# Good — plain for loop makes the intent obvious
for user in users:
    print(f"Processing user {user['name']}")

for user in users:
    requests.post("/api/notify", json=user)
python
# Bad — comprehension with logic complex enough to need comments or multiple steps
result = [
    user["name"].strip().lower()
    if user.get("active") and user.get("email_verified")
    else user["name"].strip().lower() + " (unverified)"
    for user in users
    if user.get("role") in ("admin", "editor") and user.get("last_login") is not None
]

# Good — break it out when the logic is this involved
result = []
for user in users:
    if user.get("role") not in ("admin", "editor"):
        continue
    if user.get("last_login") is None:
        continue
    name = user["name"].strip().lower()
    if not (user.get("active") and user.get("email_verified")):
        name += " (unverified)"
    result.append(name)
  • Bruk en comprehension når du bygger en ny liste fra en eksisterende iterable med et tydelig uttrykk og et valgfritt filter.
  • Bruk en for-løkke når du kjører sideeffekter — I/O, nettverkskall, utskrift, mutasjon av ekstern tilstand.
  • Bruk en for-løkke når transformasjonslogikken trenger flere linjer, mellomliggende variabler eller kommentarer for å forstås.
  • Bruk et generatoruttrykk når du bare trenger å iterere én gang eller sende direkte til sum(), any(), max() — ingen liste nødvendig i minnet.

Ytelsesmerknad

List comprehensions er meningsfullt raskere enn en tilsvarende for + append -løkke i CPython. Årsaken er bytecode: en comprehension kompilerer til en dedikert LIST_APPEND -opkode som unngår attributtoppslaget på list.append ved hver iterasjon. Python-ytelsestips-wikien dekker dette, og gapet er typisk 10–40% for ren-Python-arbeidsbelastninger avhengig av listestørrelse.

python
import timeit

data = list(range(100_000))

# for + append
def with_loop():
    result = []
    for x in data:
        result.append(x * 2)
    return result

# list comprehension
def with_comprehension():
    return [x * 2 for x in data]

# generator expression — no list built at all
def with_generator():
    return sum(x * 2 for x in data)

# Typical results on CPython 3.12:
# with_loop():          ~7.2 ms
# with_comprehension(): ~4.8 ms  (~33% faster)
# with_generator():     ~4.1 ms  (and uses O(1) memory vs O(n))

Hvis du ikke trenger en materialisert liste — du sender resultatet til sum(), any(), max(), eller itererer det én gang — bruk et generatoruttrykk i stedet. Det bruker konstant minne uavhengig av inngangssize, noe som betyr noe ved behandling av store CSV-eksporter eller JSON-nyttelaster i en tett løkke.

Oppsummering

List comprehensions er en av de Python-funksjonene som føles klosset i omtrent en uke og deretter blir umulige å leve uten. Den mentale modellen er enkel: utgangsuttrykk, løkkevariabel, iterable, valgfritt filter. Hold deg til det, og du skriver lesbar, idiomatisk Python. Når logikken blir kompleks, er det signalet til å falle tilbake til en vanlig løkke — ikke en fiasko, bare det riktige verktøyet for jobben.

Hvis du jobber med JSON-data i Python — forvandler API-svar til lister med verdier, henter ut felter fra poster, bygger oppslagsdicts — passer verktøyene på dette nettstedet godt med det du nettopp har lært. Prøv JSON Formatter for å inspisere og pretty-printe JSON-nyttelaster før du skriver comprehensionen som behandler dem, eller CSV Formatter for å validere CSV-data før du parser det til en liste med rader. For den fullstendige språkreferansen er Python-dokumentasjonen om list comprehensions og PEP 202 (det opprinnelige forslaget) verdt å lese.