Comprensioni di lista e generatori per estrarre dati unici e ordinati in Python

by theArchitect
SHARE
Comprensioni di lista e generatori per estrarre dati unici e ordinati in Python
© Guida-HTML5.it

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 for esplicito.
  • 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 set elimina 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.

SHARE