f in x
List Comprehension, Generatori e Iteratori in Python: Guida Operativa
> cd .. / HUB_EDITORIALE > Visualizza in Inglese
Analisi dei dati e metriche

List Comprehension, Generatori e Iteratori in Python: Guida Operativa

[2026-06-04] Author: Ing. Calogero Bono

Se scrivi Python e usi ancora i cicli for per costruire liste o elaborare dati, stai sprecando risorse. Lo vediamo nei progetti che ci arrivano: codice più lento, memoria occupata inutilmente, carichi server che lievitano senza motivo. Noi, di Meteora Web, ragioniamo in termini di efficienza e costo. Una list comprehension ben scritta non è solo più elegante: consuma meno cicli CPU e rende il codice manutenibile. Un generatore, invece di caricare tutto in RAM, può ridurre l'uso di memoria del 90% su dataset reali. Questa guida ti porta dritto al punto: capire come funzionano, quando usarli e quando no, con esempi che puoi copiare e testare subito.

List Comprehension: sintassi, potenza e limiti

La list comprehension è il modo Pythonico per creare liste in una riga. Sembra magia, ma è solo sintassi compatta. La struttura base è [espressione for elemento in iterabile if condizione]. L'ordine è lo stesso di un ciclo annidato: prima la for, poi eventuale if.

Esempio pratico: filtrare ordini

Immagina di avere una lista di ordini e volere solo quelli sopra i 100€. Con un ciclo classico:

ordini = [45, 120, 80, 200, 55]
ordini_filtrati = []
for prezzo in ordini:
    if prezzo > 100:
        ordini_filtrati.append(prezzo)
print(ordini_filtrati)  # [120, 200]

Con list comprehension:

ordini_filtrati = [prezzo for prezzo in ordini if prezzo > 100]
print(ordini_filtrati)  # [120, 200]

Meno righe, stessa leggibilità. Ma attenzione: non esagerare. Se la condizione è complessa o devi annidare tre for, meglio un ciclo normale. La leggibilità conta più della brevità.

Operazioni su ogni elemento

Puoi trasformare i dati al volo. Esempio: applicare IVA del 22% a una lista di prezzi netti:

netti = [10, 25, 50]
con_iva = [round(p * 1.22, 2) for p in netti]
print(con_iva)  # [12.2, 30.5, 61.0]

List comprehension nidificate

Funzionano come cicli annidati. Per appiattire una matrice:

matrice = [[1,2,3], [4,5], [6]]
appiattita = [num for riga in matrice for num in riga]
print(appiattita)  # [1, 2, 3, 4, 5, 6]

Attenzione all'ordine: prima il ciclo esterno, poi quello interno. Leggi da sinistra a destra come fossero annidati.

Generatori: quando la memoria è un problema

Una list comprehension costruisce l'intera lista in memoria. Se hai un milione di elementi, occupi diversi MB di RAM. Un generatore, invece, produce un elemento alla volta – lazy evaluation. La sintassi è identica, ma con parentesi tonde invece di quadre.

numeri = range(1_000_000)
# Generatore: non occupa memoria
gen = (x * 2 for x in numeri)
print(next(gen))  # 0
print(next(gen))  # 2

Non puoi indicizzare un generatore né conoscerne la lunghezza senza esaurirlo. Ma puoi iterarci sopra con un for o convertirlo in lista (perdendo il vantaggio).

Yield: generatori fatti su misura

Con yield crei funzioni generatrici. Esempio: leggere un file di log riga per riga senza caricarlo tutto in RAM:

def leggi_log(path):
    with open(path, 'r') as f:
        for riga in f:
            yield riga.strip()

for entry in leggi_log('server.log'):
    if 'ERROR' in entry:
        print(entry)

Ogni chiamata a next() riprende l'esecuzione dal punto in cui si era fermata. È come un ciclo congelato. Utile per pipeline di dati o flussi infiniti.

Iteratori: il motore sotto il cofano

List comprehension, generatori, cicli for – tutto si basa sugli iteratori. Un iterabile è un oggetto che restituisce un iteratore tramite iter(). L'iteratore implementa __next__() e solleva StopIteration quando finisce.

numeri = [1, 2, 3]
it = iter(numeri)
print(next(it))  # 1
print(next(it))  # 2
print(next(it))  # 3
# print(next(it))  # StopIteration

Puoi creare iteratori personalizzati definendo __iter__ e __next__. Esempio: un contatore che parte da un valore e si ferma a un limite:

class Contatore:
    def __init__(self, start, stop):
        self.current = start
        self.stop = stop
    def __iter__(self):
        return self
    def __next__(self):
        if self.current > self.stop:
            raise StopIteration
        valore = self.current
        self.current += 1
        return valore

for num in Contatore(1, 5):
    print(num)  # 1 2 3 4 5

I generatori sono un modo più semplice per scrivere iteratori, ma capire il meccanismo ti permette di debuggarli e ottimizzarli.

Confronto performance: list comprehension vs generatori

Noi misuriamo sempre. Con il modulo timeit puoi testare:

import timeit

# List comprehension: 1 milione di quadrati
list_time = timeit.timeit('[x2 for x in range(1_000_000)]', number=1)
print(f"List comprehension: {list_time:.4f} sec")  # ~0.06 sec

# Generatore: stesso calcolo, ma lazy
gen_time = timeit.timeit('(x2 for x in range(1_000_000))', number=1)
print(f"Generatore: {gen_time:.4f} sec")  # ~0.000001 sec (non esegue!)

# Se poi iteriamo il generatore
gen = (x**2 for x in range(1_000_000))
def consuma():
    for _ in gen:
        pass
gen_iter_time = timeit.timeit('consuma()', globals={'consuma': consuma}, number=1)
print(f"Generatore iterato: {gen_iter_time:.4f} sec")  # ~0.08 sec

Morale: la creazione di un generatore è istantanea, ma l'iterazione totale è simile alla list comprehension. Il vero guadagno è quando non devi consumare tutto. Ad esempio, se cerchi il primo elemento che soddisfa una condizione, un generatore si ferma subito.

Errori comuni e come evitarli

  • Usare list comprehension quando serve un generatore: se devi passare i dati a una funzione che accetta un iterabile (es. sum(), any(), max()), non serve creare una lista intermedia. Usa un generatore: sum(x**2 for x in range(10)).
  • Riutilizzare un generatore: dopo averlo esaurito, è vuoto. Se devi iterare due volte, convertilo in lista (o rinegociałlo).
  • Complessità eccessiva nelle list comprehension: se hai più di due for o condizioni, scrivi un ciclo normale con commenti. Il codice deve essere capito da altri (o da te tra 6 mesi).
  • Ignorare lo scope delle variabili: nelle list comprehension, la variabile del ciclo “perde” nel namespace (Python 2) o è locale (Python 3). In ogni caso, evita di modificare variabili esterne dentro una comprehension.

Una checklist per decidere

  1. Devo produrre una lista? → List comprehension.
  2. Devo processare un flusso di dati uno alla volta? → Generatore.
  3. Voglio solo iterare una volta e fermarmi presto? → Generatore.
  4. La logica è complessa? → Ciclo normale o funzione generatrice con yield.
  5. La leggibilità è prioritaria? → Scrivi nel modo più chiaro, anche se più lungo.

In sintesi — cosa fare adesso

Subito. Rivedi il tuo codice Python: cerca cicli for che popolano liste. Sostituisci con list comprehension dove è lineare. Dove possibile, trasforma in generatori per ridurre memoria. Testa le performance con timeit. Impara a leggere gli iterator protocol – è la base delle librerie asincrone e dei framework web. Noi, di Meteora Web, usiamo questi concetti ogni giorno nei nostri progetti Laravel e Python per gestire flussi di dati in modo efficiente, anche su server condivisi. Il risparmio di risorse si traduce in costi inferiori e clienti più soddisfatti.

Se vuoi approfondire l'uso degli iteratori in contesti reali, dai un’occhiata alla documentazione ufficiale Python su iteratori.

Sponsored Protocol

Ing. Calogero Bono

> AUTHOR_EXTRACTED

Ing. Calogero Bono

Co-founder di Meteora Web. Ingegnere informatico, sviluppo ecosistemi digitali ad alte prestazioni. AI, automazione, SEO tecnica e infrastrutture web. Scrivo di tecnologia per rendere complesso… semplice.

[ Read Full Dossier ]

Hai bisogno di applicare questa strategia?

Esegui il protocollo di contatto per iniziare un progetto con noi.

> INIZIA_PROGETTO

Sponsored

> MW_JOURNAL

> READ_ALL()