Tuple e unpacking
Una lista che non può cambiare, quando è proprio quello che serve, l'unpacking per usare i nomi al posto degli indici, lo scambio in una riga e la trappola tra (5) e (5,).
Come vuoi imparare?
Il problema che stiamo risolvendo
Servono tre giorni di meteo da registrare — per ogni giorno il nome, la temperatura e l'umidità.
Una lista fa il lavoro: ["monday", 31.5, 78]. Funziona, eppure due cose danno fastidio.
Primo, quella lista si può cambiare. Se qualcosa fa append al record di lunedì per sbaglio, Python non dice una parola — anche se un record non dovrebbe mai avere quattro cose dentro. Mettere qualcosa che non deve cambiare in un contenitore che si può cambiare è un rischio silenzioso.
Secondo, recuperare i tre valori significa scrivere record[0], record[1], record[2] — per numero, non per nome. Sei mesi dopo, chi legge record[1] non saprà se è la temperatura o l'umidità.
Una tupla risponde a entrambe: è una lista che non può essere cambiata — e porta con sé un'abilità chiamata unpacking, che ti permette di usare i nomi al posto degli indici.
Alla fine di questo capitolo sarai in grado di
- Costruire una tupla, e dire dove la tupla è la scelta giusta e la lista no
- Mettere più valori in più nomi in una volta sola con l'unpacking
- Scambiare due variabili in una riga
- Distinguere
(5)da(5,) - Spiegare perché
appendsu una tupla solleva unAttributeError
Prerequisiti: Ordinare le liste, e i metodi che le accompagnano.
Una tupla — una lista che non può essere cambiata
Parentesi tonde al posto di quadre:
point = (3, 7)
print(point)
print(point[0])
print(len(point))(3, 7)
3
2Lettura, accesso per indice, len, slice, in, cicli con for — tutto ciò che facevi con una lista funziona. C'è una sola differenza, ed è il cuore della questione:
point = (3, 7)
point[0] = 5TypeError: 'tuple' object does not support item assignmentTi suona familiare quel messaggio? È parola per parola quello del capitolo sei, quando è stato cambiato un carattero dentro una stringa, con 'str' al posto di 'tuple'. Tutte le cose immutabili di Python si lamentano con le stesse parole.
Anche i metodi che cambiano le cose mancano:
point = (3, 7)
point.append(9)AttributeError: 'tuple' object has no attribute 'append'Un AttributeError significa "questo oggetto non ha niente con quel nome". Una tupla non può essere cambiata, quindi una tupla non ha affatto un metodo chiamato append. Python si ferma mentre cerca point.append, prima ancora di provare ad aggiungere 9. Confrontalo con l'esempio precedente: point[0] = 5 chiedeva un'operazione che una tupla non permette, quindi era un TypeError; qui il metodo stesso non esiste, quindi è un AttributeError.
Perché è un vantaggio e non un limite
All'inizio una tupla sembra una lista a cui sono state tolte delle caratteristiche. Vediamola al contrario: se qualcosa non deve cambiare, metterlo dove non può cambiare ferma l'errore prima che accada.
Non ha senso aggiungere un terzo numero alla coordinata (3, 7). Una data (2026, 9, 16) non ha una quarta parte. Tenuti in tuple, quegli incidenti arrivano subito come errori invece che come bug più avanti.
Come regola: un numero fisso di cose, ognuna con un significato diverso — tupla. Un numero imprecisato di cose, tutte dello stesso tipo — lista.
Quindi tre giorni di meteo sono tuple dentro una lista: il numero di giorni non si conosce (una lista), ma ogni record contiene esattamente tre cose (una tupla).
L'unpacking — dove sta il vero vantaggio
point = (3, 7)
x, y = point
print(x)
print(y)3
7Due nomi e due valori su una riga. E adesso è y e non point[1] — un nome che si spiega da solo quando lo leggi.
Sulla sinistra ci devono essere tanti nomi quanti sono i valori sulla destra:
point = (3, 7)
x, y, z = pointValueError: not enough values to unpack (expected 3, got 2)Quello è un messaggio insolitamente utile — ti dice sia quanti se ne aspettavano sia quanti sono arrivati.
L'unpacking non è limitato alle tuple; funziona anche con le liste. È più utile con le tuple, perché è lì che il numero di valori si conosce in anticipo.
Scambiare, in una riga
Altri linguaggi hanno bisogno di una terza variabile temporanea per scambiare due valori. Python no:
a = 1
b = 2
a, b = b, a
print(a, b)2 1Vale la pena capire come funziona la riga: Python valuta per primo l'intero lato destro, costruendo la tupla (2, 1), e poi ne fa l'unpacking a sinistra. Così, anche se a cambia, il vecchio valore di b è già stato salvato dentro quella tupla, e niente va perso.
Le parentesi in realtà non servono
point = 3, 7
print(point)
print(type(point))(3, 7)
<class 'tuple'>Ciò che fa la tupla è la virgola, non le parentesi. Le parentesi ci sono per la leggibilità. Una volta che lo sai, a, b = b, a smette di sembrare magia — b, a sulla destra è semplicemente una tupla.
E da qui nasce una trappola:
one = (5)
really = (5,)
print(type(one))
print(type(really))
print(len(really))<class 'int'>
<class 'tuple'>
1(5) non è una tupla — è un numero tra parentesi, esattamente come (2 + 3). Costruire una tupla da un solo valore richiede la virgola: (5,). Sembra un refuso, ed è quella virgola a fare tutta la differenza.
Tuple in un ciclo
people = [("rafi", 22), ("ahmed", 35)]
for name, age in people:
print(f"{name} is {age}")rafi is 22
ahmed is 35for name, age in people fa l'unpacking di ogni tupla direttamente in due nomi. Niente person[0] e person[1] — e il codice si spiega da solo quando lo leggi.
Lo hai già usato senza saperlo:
stock = ["pen", "bag"]
for pair in enumerate(stock):
print(pair)(0, 'pen')
(1, 'bag')enumerate restituisce tuple. Scrivere for position, item in enumerate(...) nel capitolo dodici significava fare il loro unpacking — semplicemente non avevamo un nome per quella cosa.
Convertire tra le due
as_list = [3, 7]
as_tuple = (3, 7)
as_list.append(9)
print(as_list)
print(as_tuple)
print(list(as_tuple))
print(tuple(as_list))[3, 7, 9]
(3, 7)
[3, 7]
(3, 7, 9)list() e tuple() ne costruiscono una dall'altra — una cosa nuova ogni volta, lasciando intatta l'originale. È così che si "cambia" una tupla: si crea una lista, la si cambia, si crea di nuovo una tupla.
Un esempio completo
weather.py:
# A tuple is the right shape for a record that must not change
readings = [
("monday", 31.5, 78),
("tuesday", 33.0, 71),
("wednesday", 29.5, 85),
]
print("Day Temp Humidity")
for day, temp, humidity in readings:
print(f"{day:<10} {temp:>5} {humidity:>5}")
hottest = readings[0]
for reading in readings:
if reading[1] > hottest[1]:
hottest = reading
day, temp, humidity = hottest
print()
print(f"Hottest was {day} at {temp}")
temps = []
for _, temp, _ in readings:
temps.append(temp)
print("Temps :", temps)
print("Average :", round(sum(temps) / len(temps), 2))Day Temp Humidity
monday 31.5 78
tuesday 33.0 71
wednesday 29.5 85
Hottest was tuesday at 33.0
Temps : [31.5, 33.0, 29.5]
Average : 31.33Quattro cose da notare.
Una lista fuori, tuple dentro. Il numero di giorni può crescere, quindi la cosa esterna è una lista. Ogni record contiene esattamente tre cose, quindi quelle interne sono tuple. Questa è la forma più comune in cui arrivano i dati veri.
f"{day:<10}" e f"{temp:>5}" tengono dritte le colonne. < allinea a sinistra, > allinea a destra, e il numero indica quanta larghezza occupare. Come il :.2f del capitolo sei, tutto ciò che segue i due punti è formattazione.
Trovare il giorno più caldo ha richiesto reading[1], perché durante il confronto si ha in mano la tupla intera e non i suoi valori già separati. L'unpacking avviene appena la tupla è stata trovata — ed è per questo che la riga dopo non contiene alcun indice.
for _, temp, _ in readings — il trattino basso è una convenzione che significa "questo non mi serve". Per Python è un nome qualunque; per chi legge indica quali parti vengono buttate via di proposito.
Quando qualcosa va storto
TypeError: 'tuple' object does not support item assignment Un tentativo di cambiare un valore dentro una tupla. Le tuple non si possono cambiare — o usi una lista dall'inizio, o converti con list(...), la cambi e converti di nuovo.
AttributeError: 'tuple' object has no attribute 'append' La stessa causa con altri panni. Nessun metodo che cambia — append, remove, sort — esiste su una tupla. sorted(my_tuple) funziona, perché restituisce una nuova lista e lascia la tupla intatta.
ValueError: not enough values to unpack (expected 3, got 2) Ci sono più nomi a sinistra che valori a destra. print(len(record)) mostra quanti ce ne sono davvero.
ValueError: too many values to unpack (expected 2) Il contrario — più valori che nomi.
Ho fatto una tupla da un solo valore e non è una tupla Manca la virgola. (5) è un numero; (5,) è una tupla.
Una lista dentro la mia tupla è cambiata Una tupla protegge la propria struttura — quali cose contiene non può cambiare. Ma se una di quelle cose è essa stessa modificabile, come una lista, l'interno di quella lista può cambiare lo stesso. In ([1, 2], 3) puoi fare append alla prima lista.
Passo 4 di 6 — Prevedi
Verifica cosa hai capito
Cosa viene stampato dopo l'unpacking?
point = (3, 7)
x, y = point
print(y, x)- A3 7
- B(3, 7)
- CUn `ValueError`
- D7 3
Un tentativo di append su una tupla. Quale errore arriva, e perché proprio quello?
point = (3, 7)
point.append(9)- AUn `ValueError`
- BNon succede niente; la tupla resta invariata
- CUn `AttributeError` — una tupla non può cambiare, quindi i metodi che cambiano semplicemente non esistono su di essa
- DUn `TypeError` — `append` non è consentito su una tupla
Le due righe differiscono di una virgola. Cosa riporta type()?
one = (5)
really = (5,)
print(type(one))
print(type(really))- A<class 'int'> <class 'int'>
- B<class 'tuple'> <class 'tuple'>
- C<class 'int'> <class 'tuple'>
- D<class 'tuple'> <class 'int'>
Per rispondere serve un account
Accedi per verificare le tue risposte
Le domande sono qui sopra, e ragionarci a mente è la parte che conta. Accedi per vedere le risposte, le spiegazioni e i suggerimenti a tre livelli.
Tocca a te
Scrivi un file chiamato contacts.py che contiene una lista di almeno cinque persone, ognuna una tupla con un nome, un'età e una materia preferita.
Poi:
- Stampa tutte le persone in colonne allineate, usando
for name, age, subject in contacts: - Trova la persona più vecchia, fai il suo unpacking e stampa una frase a riguardo
- Costruisci una lista con solo i nomi e stampala su una riga con
join - Stampa l'età media
- Scambia due variabili in una riga e dimostra che ha funzionato
Poi rompilo tre volte di proposito, e leggi ogni messaggio:
- Prova a cambiare l'età dentro una delle tuple
- Prova a fare
appenda una tupla - Prova a fare l'unpacking di una tupla da tre valori in due nomi
Torneranno indietro tre errori diversi — TypeError, AttributeError, ValueError. Riuscire a spiegarti quale è quale, e perché, significa che le tuple sono tue.
Passo 6 di 6
Sfida — il quiz del capitolo
Dieci domande dalla più facile alla più difficile. Le ultime sono difficili di proposito.
Accedi per fare il quiz