W3docs

Python enumerate, zip, map e filter

Scopri come funzionano enumerate, zip, map e filter in Python, con esempi chiari e pattern pratici per un codice di iterazione più leggibile.

Python include quattro funzioni built-in — enumerate, zip, map e filter — che sostituiscono i pattern comuni dei cicli con un'unica espressione leggibile. Ognuna restituisce un iteratore (lazy, efficiente in memoria) e si combina naturalmente con i cicli for e le list comprehension.

Questo capitolo tratta:

  • enumerate() — ciclo con indice automatico
  • zip() — iterare su due o più sequenze in parallelo
  • map() — applicare una funzione a ogni elemento
  • filter() — mantenere solo gli elementi che superano un test
  • Combinare tutte e quattro in pattern reali
  • Quando preferire una list comprehension

enumerate() — indice durante l'iterazione

Quando si ha bisogno sia della posizione sia del valore durante un ciclo, l'approccio ingenuo usa un contatore manuale:

colors = ["red", "green", "blue"]
i = 0
for color in colors:
    print(i, color)
    i += 1

enumerate() lo fa automaticamente. Avvolge qualsiasi iterabile e produce coppie (index, value):

colors = ["red", "green", "blue"]
for i, color in enumerate(colors):
    print(i, color)

Output:

0 red
1 green
2 blue

Partire da un indice diverso

Passa un argomento start per iniziare il conteggio da qualsiasi intero:

fruits = ["apple", "banana", "cherry"]
for i, fruit in enumerate(fruits, start=1):
    print(f"{i}. {fruit}")

Output:

1. apple
2. banana
3. cherry

Questo è utile quando si costruiscono liste numerate da visualizzare, dove la numerazione a base 0 risulta strana per il lettore.

enumerate() con una condizione

Poiché enumerate() è semplicemente un iteratore ordinario, puoi aggiungere test if all'interno del corpo del ciclo:

scores = [72, 91, 55, 88, 44]
for rank, score in enumerate(scores, start=1):
    if score >= 80:
        print(f"Rank {rank}: score {score} — PASS")

Output:

Rank 2: score 91 — PASS
Rank 4: score 88 — PASS

Ottenere solo l'indice senza enumerate()

Se hai bisogno solo di un indice e nient'altro, range(len(seq)) funziona — ma non appena hai bisogno anche del valore, usa enumerate() per evitare di scrivere seq[i] ripetutamente.


zip() — iterare su più sequenze in parallelo

zip() combina due o più iterabili in un unico iteratore di tuple, abbinando gli elementi alle posizioni corrispondenti:

names = ["Alice", "Bob", "Charlie"]
scores = [95, 87, 92]

for name, score in zip(names, scores):
    print(f"{name}: {score}")

Output:

Alice: 95
Bob: 87
Charlie: 92

zip() con tre o più iterabili

first = ["a", "b", "c"]
second = [1, 2, 3]
third = [True, False, True]

for x, y, z in zip(first, second, third):
    print(x, y, z)

Output:

a 1 True
b 2 False
c 3 True

Sequenze di lunghezza diversa

zip() si ferma all'iterabile più corto e scarta silenziosamente gli elementi rimanenti da quelli più lunghi:

short = [1, 2]
long  = [10, 20, 30, 40]

print(list(zip(short, long)))

Output:

[(1, 10), (2, 20)]

I valori 30 e 40 da long vengono scartati. Se hai bisogno di conservare tutti gli elementi, usa itertools.zip_longest dalla libreria standard:

from itertools import zip_longest

short = [1, 2]
long  = [10, 20, 30, 40]

print(list(zip_longest(short, long, fillvalue=0)))

Output:

[(1, 10), (2, 20), (0, 30), (0, 40)]

Convertire il risultato di zip in una lista o un dizionario

zip() restituisce un iteratore lazy. Avvolgilo in list() per materializzare tutte le coppie contemporaneamente, oppure passalo a dict() per costruire una mappatura da due sequenze parallele:

keys   = ["name", "age", "city"]
values = ["Alice", 30, "Paris"]

record = dict(zip(keys, values))
print(record)

Output:

{'name': 'Alice', 'age': 30, 'city': 'Paris'}

Decomprimere con zip(*...)

Il trucco di unpacking * "inverte" una sequenza di tuple separandole in sequenze distinte:

pairs = [(1, "a"), (2, "b"), (3, "c")]
numbers, letters = zip(*pairs)

print(numbers)  # (1, 2, 3)
print(letters)  # ('a', 'b', 'c')

map() — applicare una funzione a ogni elemento

map(function, iterable) applica function a ogni elemento e restituisce un iteratore lazy dei risultati. Sostituisce un ciclo for che costruisce una nuova lista trasformando ogni elemento.

numbers = [1, 2, 3, 4, 5]
squares = map(lambda x: x ** 2, numbers)
print(list(squares))

Output:

[1, 4, 9, 16, 25]

map() con una funzione con nome

Puoi passare qualsiasi callable — una funzione built-in, una funzione definita con def o una lambda:

words = ["  hello  ", "  world  ", "  python  "]
stripped = list(map(str.strip, words))
print(stripped)

Output:

['hello', 'world', 'python']

Qui str.strip viene passata come riferimento (senza parentesi). map() chiama str.strip(word) per ogni elemento.

map() su più iterabili

Passa iterabili aggiuntivi per mappare una funzione che accetta due o più argomenti. map() scorre tutti gli iterabili in parallelo (come zip(), si ferma al più corto):

a = [1, 2, 3]
b = [10, 20, 30]
totals = list(map(lambda x, y: x + y, a, b))
print(totals)

Output:

[11, 22, 33]

map() vs list comprehension

Entrambi gli approcci sono validi. La regola generale:

SituazionePreferire
Applicare una funzione con nome già esistentemap() — più breve, nessuna lambda necessaria
Scrivere un'espressione inlineList comprehension — più leggibile
Più input in parallelomap() con più iterabili

Forme equivalenti:

# map() with a named function
result = list(map(str.upper, ["a", "b", "c"]))

# list comprehension — same result
result = [s.upper() for s in ["a", "b", "c"]]

filter() — mantenere solo gli elementi che corrispondono a una condizione

filter(function, iterable) mantiene gli elementi per cui function restituisce un valore truthy. Restituisce un iteratore lazy.

numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
evens = filter(lambda x: x % 2 == 0, numbers)
print(list(evens))

Output:

[2, 4, 6, 8, 10]

filter() con una funzione con nome

def is_positive(n):
    return n > 0

values = [-3, -1, 0, 4, 7, -2]
positives = list(filter(is_positive, values))
print(positives)

Output:

[4, 7]

filter(None, iterable) — rimuovere i valori falsy

Passare None come funzione mantiene solo gli elementi truthy. Questo è un modo rapido per eliminare stringhe vuote, zeri e valori None:

mixed = [0, "hello", "", None, 42, False, "world"]
truthy = list(filter(None, mixed))
print(truthy)

Output:

['hello', 42, 'world']

filter() vs list comprehension

filter() e una list comprehension condizionale svolgono lo stesso compito:

numbers = range(1, 11)

# filter()
evens_f = list(filter(lambda x: x % 2 == 0, numbers))

# list comprehension — equivalent
evens_c = [x for x in numbers if x % 2 == 0]

print(evens_f)  # [2, 4, 6, 8, 10]
print(evens_c)  # [2, 4, 6, 8, 10]

Preferisci una list comprehension quando la condizione è scritta inline; preferisci filter() quando hai già una funzione predicato ben nominata.


Combinare enumerate, zip, map e filter

Queste quattro funzioni si compongono naturalmente. Ecco un esempio realistico: hai due liste parallele di nomi di studenti e punteggi grezzi; vuoi stampare un report numerato che include solo gli studenti che hanno superato l'esame (punteggio >= 60), con i punteggi convertiti in voti letterali.

def letter_grade(score):
    if score >= 90:
        return "A"
    elif score >= 80:
        return "B"
    elif score >= 70:
        return "C"
    elif score >= 60:
        return "D"
    return "F"

names  = ["Alice", "Bob", "Carol", "Dave", "Eve"]
scores = [85, 52, 91, 67, 48]

# Step 1: pair names with scores
pairs = zip(names, scores)

# Step 2: keep only passing students
passing = filter(lambda pair: pair[1] >= 60, pairs)

# Step 3: convert scores to letter grades
graded = map(lambda pair: (pair[0], letter_grade(pair[1])), passing)

# Step 4: number the results
for rank, (name, grade) in enumerate(graded, start=1):
    print(f"{rank}. {name}: {grade}")

Output:

1. Alice: B
2. Carol: A
3. Dave: D

Ogni passaggio produce un iteratore; nulla viene materializzato in una lista finché il ciclo for non lo consuma. Questo mantiene l'utilizzo della memoria costante indipendentemente dalla dimensione dell'input.


Errori comuni

Gli iteratori si esauriscono dopo un solo passaggio

map(), filter() e zip() restituiscono tutti iteratori, non liste. Una volta consumato un iteratore, è vuoto:

squares = map(lambda x: x ** 2, [1, 2, 3])
print(list(squares))  # [1, 4, 9]
print(list(squares))  # [] — already exhausted

Avvolgi in list() in anticipo se devi iterare più di una volta.

zip() scarta silenziosamente gli elementi finali

Quando le sequenze di input hanno lunghezze diverse, zip() scarta gli elementi extra senza avvisi. Controlla sempre esplicitamente le lunghezze se questo è importante, oppure usa itertools.zip_longest.

enumerate() non modifica l'iterabile

enumerate() avvolge l'iterabile originale; non lo copia. Modificare la lista sottostante durante l'iterazione può produrre risultati inattesi — la stessa cautela che si applica ai normali cicli for.


Riferimento rapido

FunzioneFirmaCosa restituisce
enumerateenumerate(iterable, start=0)Iteratore di tuple (index, value)
zipzip(*iterables)Iteratore di tuple, un elemento da ogni iterabile per tupla
mapmap(function, *iterables)Iteratore di function applicata a ogni elemento
filterfilter(function, iterable)Iteratore degli elementi in cui function restituisce True

Tutte e quattro sono lazy: producono valori su richiesta invece di costruire una lista completa in anticipo.


Esercitazione

Pratica
What does enumerate(items, start=1) do when used in a for loop?
What does enumerate(items, start=1) do when used in a for loop?
Pratica
What happens when you pass sequences of different lengths to zip()?
What happens when you pass sequences of different lengths to zip()?
Pratica
Which statement about map() is correct?
Which statement about map() is correct?
Pratica
What does filter(None, iterable) do?
What does filter(None, iterable) do?
Was this page helpful?