Corso Python / Insiemi: unicità, appartenenza e operazioni con set

Un insieme Python, o set, conserva elementi unici e permette di verificare appartenenza, unione, intersezione e differenza. Convertire dati in un insieme elimina ripetizioni e informazioni di posizione: fallo soltanto quando quei dettagli non servono.

Creare un insieme e leggere le operazioni

Per un insieme non vuoto puoi scrivere {"food", "books"}; per uno vuoto usa set(). Le sole parentesi graffe {} creano invece un dizionario. Gli elementi devono essere hashable: liste e dizionari non possono essere inseriti direttamente in un insieme. La guida alle chiavi dei dizionari chiarisce questa condizione.

Un insieme non assegna indici ai suoi elementi e non registra un ordine di inserimento su cui basare un contratto dei dati. Non usare quindi used[0] per cercare il «primo» elemento, né considerare l’ordine di una singola stampa una garanzia per le esecuzioni successive.

Espressione Significato
a | b Unione: elementi presenti in almeno uno dei due insiemi.
a & b Intersezione: elementi presenti in entrambi.
a - b Differenza: elementi presenti in a e assenti in b.
a ^ b Differenza simmetrica: elementi presenti in uno solo dei due insiemi.

Questi operatori restituiscono un nuovo insieme senza cambiare gli operandi. Le regole dei due tipi set e frozenset sono descritte nella documentazione ufficiale degli insiemi.

Confrontare categorie usate e categorie ammesse

Salva il programma seguente in UTF-8 come sets.py, in una cartella di esercitazione. Esegui python sets.py con l’interprete del progetto selezionato. Fuori dall’ambiente virtuale su macOS o Ubuntu usa python3 se è il comando dell’interprete scelto. Il programma non richiede pacchetti esterni.

used = {"food", "transport", "food"}
allowed = {"food", "books"}
print(sorted(used))
print(sorted(used & allowed))
print(sorted(used - allowed))
used.discard("missing")
print("food" in used)

Il risultato atteso è:

['food', 'transport']
['food']
['transport']
True

Il valore food compare due volte nella creazione di used, ma resta un solo elemento. L’intersezione con allowed trova food, mentre la differenza used - allowed identifica transport come categoria non ammessa. L’ultima riga verifica l’appartenenza e restituisce True.

sorted() rende stabile l’ordine di visualizzazione restituendo una lista ordinata. Non trasforma l’insieme stesso in una struttura ordinata. Nell’esempio tutti gli elementi sono stringhe confrontabili; con tipi misti non confrontabili l’ordinamento può fallire.

Distinguere unicità, frequenza e identità

Un insieme risponde alla domanda «questo valore compare?», non alla domanda «quante volte compare?». Se hai due spese distinte di 8000, la lista [8000, 8000] ha totale 16000, ma l’insieme {8000} ha totale 8000. La deduplicazione per importo cancellerebbe un’operazione reale.

La verifica di ID duplicati è un altro compito: un identificatore stabile permette di riconoscere la stessa transazione, mentre due importi uguali possono appartenere a transazioni diverse. Decidi se un ID ripetuto debba essere rifiutato, aggiornato o segnalato; non lasciare che una conversione a set nasconda automaticamente la ripetizione.

Per elementi hashable, list(dict.fromkeys(items)) rimuove i duplicati mantenendo l’ordine della prima occorrenza. Anche questa trasformazione perde il numero di ripetizioni. Se la frequenza è significativa, conservala o calcolala prima di deduplicare.

Gestire rimozioni e scegliere la struttura adatta

remove() genera KeyError se l’elemento manca; discard() non genera errore in quel caso. Usa il primo comportamento quando l’assenza rivela un requisito violato e il secondo quando l’elemento è facoltativo. Entrambi modificano l’insieme e restituiscono None, quindi non assegnarne il risultato al nome dell’insieme.

  1. Aggiungi used.add("food") subito dopo la creazione di used. Verifica che gli elementi e la lunghezza non aumentino: food era già presente.
  2. Ripristina il programma e aggiungi invece used.add("books"). L’intersezione ordinata diventa ['books', 'food'], mentre la categoria non ammessa resta transport.
  3. Prova separatamente a rimuovere "missing" con remove() per osservare KeyError. Ripristina il discard() originale ed esegui nuovamente il programma.

Usa una lista quando contano ordine e ripetizioni, una tupla per un piccolo record posizionale, un dizionario per l’accesso per chiave e un insieme per unicità e appartenenza. La struttura adatta è quella che conserva le informazioni necessarie, non quella che rende più corta una singola istruzione.