Introduzione
Le comprensioni di lista e i generatori sono strumenti molto potenti in Python per scrivere codice compatto, leggibile e spesso più efficiente. In molti tutorial si mostrano esempi banali come filtrare una lista o trasformare valori, ma un uso davvero pratico e molto utile è la normalizzazione di dati con rimozione dei duplicati e ordinamento.
Questo scenario è comune quando lavori con dati provenienti da file CSV, API, log applicativi o input utente. Per esempio, potresti ricevere:
- nomi scritti con maiuscole e minuscole diverse;
- valori ripetuti più volte;
- spazi extra all’inizio o alla fine;
- elementi vuoti da scartare.
Con le comprensioni di lista puoi pulire e trasformare rapidamente i dati. Con i generatori, invece, puoi fare lo stesso in modo lazy, cioè producendo i valori solo quando servono. Questo è molto utile quando il dataset è grande o quando vuoi costruire una pipeline di elaborazione più efficiente.
Codice completo
from collections import OrderedDict
# Dati grezzi: nomi duplicati, spazi inutili e formati diversi
raw_names = [
" Alice",
"bob",
"ALICE ",
"",
" carol",
"Bob",
"dave",
"carol ",
"Eve",
" eve"
]
# 1) Comprensione di lista: pulizia iniziale
# - rimuove spazi
# - scarta stringhe vuote
cleaned_names = [name.strip() for name in raw_names if name.strip()]
# 2) Normalizzazione: rendiamo tutto minuscolo per confrontare correttamente
normalized_names = [name.lower() for name in cleaned_names]
# 3) Rimozione duplicati mantenendo l´ordine di apparizione
# Usiamo OrderedDict per conservare il primo incontro di ogni valore
unique_names = list(OrderedDict.fromkeys(normalized_names))
# 4) Ordinamento finale
sorted_unique_names = sorted(unique_names)
print("Puliti: ", cleaned_names)
print("Normalizzati: ", normalized_names)
print("Unici in ordine: ", unique_names)
print("Unici ordinati: ", sorted_unique_names)
print("n--- Versione con generatore ---")
# Funzione generatore: pulisce e normalizza un elemento alla volta
def iter_normalized_names(values):
for name in values:
stripped = name.strip()
if stripped:
yield stripped.lower()
# Il generatore non crea subito una lista completa
name_generator = iter_normalized_names(raw_names)
# Per rimuovere duplicati mantenendo l´ordine, usiamo un set di supporto
seen = set()
unique_from_generator = []
for name in name_generator:
if name not in seen:
seen.add(name)
unique_from_generator.append(name)
print("Unici da generatore:", unique_from_generator)
# Se vogliamo ordinare anche questi dati:
print("Ordinati da generatore:", sorted(unique_from_generator)) Spiegazione
Vediamo il codice con calma. L’obiettivo è trasformare una lista sporca di nomi in una lista pulita, senza duplicati e, se necessario, ordinata.
1. Pulizia con comprensione di lista
La prima comprensione è questa:
[name.strip() for name in raw_names if name.strip()] Qui succedono due cose:
- name.strip() elimina gli spazi iniziali e finali;
- if name.strip() scarta le stringhe vuote dopo la pulizia.
È una soluzione molto compatta e leggibile, perfetta quando vuoi esprimere in una sola riga una trasformazione semplice.
2. Normalizzazione per confronti corretti
Dopo aver pulito i dati, li convertiamo in minuscolo con:
[name.lower() for name in cleaned_names] Questo passaggio è importante perché "Alice", "ALICE" e "alice" devono essere considerati lo stesso valore se stiamo cercando duplicati logici.
3. Rimozione dei duplicati mantenendo l’ordine
In Python, un set elimina i duplicati, ma non conserva l’ordine. Se vuoi mantenere il primo ordine di apparizione, puoi usare:
list(OrderedDict.fromkeys(normalized_names)) Questa tecnica è utile quando l’ordine originale contiene un significato, ad esempio:
- priorità di visualizzazione;
- ordine di arrivo dei dati;
- sequenza temporale degli eventi.
In Python 3.7+ anche i dizionari normali mantengono l’ordine di inserimento, quindi spesso puoi usare direttamente dict.fromkeys(...). Tuttavia, OrderedDict resta chiaro e leggibile per chi studia questo pattern.
4. Ordinamento finale
Se vuoi un elenco alfabetico, puoi usare:
sorted(unique_names) Ricorda che ordinare e mantenere l’ordine originale sono due obiettivi diversi. Prima scegli quale risultato ti serve davvero.
5. Versione con generatore
La funzione:
def iter_normalized_names(values):
for name in values:
stripped = name.strip()
if stripped:
yield stripped.lower() restituisce un generatore. Questo significa che i valori vengono prodotti uno alla volta, solo quando il ciclo li richiede. Il vantaggio è evidente con grandi quantità di dati: non devi costruire subito una lista intera in memoria.
Nel blocco successivo usiamo un set chiamato seen per ricordare quali nomi sono già stati incontrati. Così possiamo eliminare i duplicati in modo efficiente durante la scansione.
Best practice
- Usa le comprensioni di lista quando la trasformazione è semplice e leggibile in una sola riga.
- Evita comprensioni troppo complesse: se la logica diventa difficile da leggere, meglio passare a un ciclo
foresplicito. - Normalizza prima di confrontare: ad esempio con
lower(),strip()o altre regole specifiche del dominio. - Usa i generatori per flussi di dati grandi o quando vuoi elaborare i valori in modo progressivo.
- Ricorda la differenza tra ordine e unicità: un
setelimina duplicati ma non conserva l’ordine, mentre un dizionario sì. - Se il dataset è piccolo, una lista può essere più semplice; se è grande o proviene da una sorgente esterna, il generatore è spesso più adatto.
Riepilogo
In questo tutorial abbiamo visto un caso d’uso molto pratico: estrarre dati unici e ordinati partendo da input sporchi o non uniformi.
- Le comprensioni di lista sono ideali per pulire e trasformare i dati in modo conciso.
- I generatori permettono di elaborare i valori uno alla volta, riducendo l’uso di memoria.
- Per rimuovere duplicati mantenendo l’ordine, puoi usare OrderedDict.fromkeys() o una combinazione di set e ciclo.
- Per ottenere un output finale leggibile, spesso conviene applicare anche un ordinamento con sorted().
Questo pattern è molto utile in applicazioni reali: pulizia di nomi, normalizzazione di categorie, deduplicazione di tag, gestione di email, codici prodotto e molto altro.
Approfondisci con risorse ufficiali
- Python Tutorial - Data Structures: documentazione ufficiale sulle liste e sugli insiemi.
- Python Tutorial - List Comprehensions: guida ufficiale alle comprensioni di lista.
- Python Tutorial - Generators: introduzione ufficiale ai generatori e a
yield. - collections.OrderedDict: documentazione ufficiale per mantenere l’ordine delle chiavi.
- Built-in Functions: sorted(): riferimento ufficiale per l’ordinamento dei dati.
