Liste — molti valori sotto un solo nome
Quanti valori vuoi sotto un solo nome, come entrarci dentro con un indice e uno slice, i metodi che modificano una lista, e la trappola di due nomi che finiscono attaccati alla stessa lista.
Come vuoi imparare?
Il problema che stiamo risolvendo
Un negozio deve tenere i prezzi di tre prodotti. Con tutto quello che abbiamo imparato finora c'è un solo modo per farlo:
price_1 = 15
price_2 = 60
price_3 = 850
print(price_1 + price_2 + price_3)925Ora la domanda: che dire di quaranta prodotti? Quaranta variabili. E una riga di aritmetica con quaranta pezzi dentro. E quando il negozio inizia a vendere qualcosa di nuovo, devi entrare nel codice e inventare un'altra variabile.
Il problema non è solo la digitazione, è la forma. Per Python, price_1, price_2 e price_3 sono tre nomi del tutto scollegati — tanto scollegati quanto name e age. Tu sai che sono tre istanze della stessa cosa. Semplicemente non abbiamo ancora un modo per scrivere "tante della stessa cosa" nel codice.
Una lista è quel modo. Un solo nome, sotto di esso tutti i valori che vuoi, in ordine.
Alla fine di questo capitolo sarai in grado di
- Costruire una lista e entrarci dentro con un indice e uno slice
- Modificare una lista con
append,insert,removeepop - Spiegare perché una lista si può modificare mentre un pezzo di testo no
- Riconoscere ed evitare che
b = alasci due nomi attaccati a una sola lista - Leggere un
IndexErrore unValueErrore dire cosa è andato storto
Prerequisiti: cicli for e range.
Creare una lista
Parentesi quadre, con i valori separati da virgole:
prices = [15, 60, 850]
print(prices)
print(len(prices))[15, 60, 850]
3len() funziona qui esattamente come sul testo — tre valori, quindi 3.
La stampa mostra l'intera lista, parentesi comprese. Non è pensata per un utente che la legge, è pensata per te che capisci: [15, 60, 850] ti dice che questa è una lista, non 925.
Accedere agli elementi
L'indicizzazione funziona esattamente come per le stringhe — si conta da zero, con i negativi dalla coda:
prices = [15, 60, 850]
print(prices[0])
print(prices[-1])
print(prices[1:])15
850
[60, 850]Tutte e tre le righe sono le regole del capitolo sei, invariate. Una differenza merita attenzione: prices[0] restituisce un numero, mentre prices[1:] restituisce una lista. Uno slice restituisce sempre lo stesso tipo di cosa da cui è stato tagliato, mai un singolo pezzo.
E qui sta la vera differenza: una lista si può modificare
Nel capitolo sei abbiamo visto che un carattere dentro il testo non si può sostituire:
name = "rafi"
name[0] = "R"TypeError: 'str' object does not support item assignmentLa stessa riga funziona su una lista:
prices = [15, 60, 850]
prices[0] = 20
print(prices)[20, 60, 850]Non è stata creata una nuova lista — questa lista è cambiata. Questa è la proprietà più importante che ha una lista, e la trappola alla fine di questo capitolo nasce proprio da lì.
I termini per dirlo: il testo è immutabile, una lista è mutabile.
Aggiungere e togliere
stock = ["pen", "notebook"]
stock.append("bag")
stock.insert(1, "eraser")
print(stock)
stock.remove("pen")
print(stock)
last = stock.pop()
print(last)
print(stock)['pen', 'eraser', 'notebook', 'bag']
['eraser', 'notebook', 'bag']
bag
['eraser', 'notebook']Quattro metodi, quattro lavori diversi:
append(x)— aggiunge alla fineinsert(i, x)— lo mette in posizionei, spostando tutto ciò che segue verso destraremove(x)— cerca il primo elemento con quel valore e lo togliepop()— toglie l'ultimo elemento e te lo restituisce
La differenza tra questi ultimi due è dove vive la maggior parte della confusione: remove vuole sapere cosa togliere, pop vuole sapere da dove. E pop ti dà la cosa che ha rimosso; remove no.
Questi metodi non restituiscono la lista. Con il testo dovevi conservare il risultato diname.upper(), perché costruiva nuovo testo e te lo restituiva. Con le liste è il contrario:appendmodifica la lista stessa e restituisceNone.
Il che rende questo un errore classico:
stock = ["pen", "notebook"]
stock = stock.append("bag")
print(stock)NoneLa lista è sparita. Scrivi stock.append("bag"), non stock = stock.append("bag").
Cercare e percorrere
stock = ["pen", "notebook", "bag"]
print("pen" in stock)
print("chair" in stock)
for item in stock:
print(item.upper())True
False
PEN
NOTEBOOK
BAGin cercava dentro il testo, e cerca anche in una lista — la stessa parola che fa lo stesso lavoro.
E for item in stock percorre i valori uno alla volta. Nota che non c'è range né aritmetica sugli indici, esattamente come quando nel capitolo undici abbiamo percorso i caratteri di una parola.
Quando ti serve la posizione e il valore, c'è enumerate:
stock = ["pen", "notebook"]
for position, item in enumerate(stock, start=1):
print(position, item)1 pen
2 notebookSenza start=1 il conteggio parte da zero. Una lista mostrata a una persona di solito deve partire da uno.
La trappola che prende tutti
Nel capitolo quattro abbiamo visto questo:
a = 3
b = a
b = b + 1
print(a, b)3 4b ha ricevuto un valore e non stava guardando a. Adesso fai la stessa cosa con una lista:
a = [1, 2, 3]
b = a
b.append(4)
print(a)
print(b)[1, 2, 3, 4]
[1, 2, 3, 4]b è cambiata, e a è cambiata anche lei.
Il motivo non contraddice la regola precedente, è la regola precedente. Nella riga b = a, b ha ricevuto esattamente ciò che a conteneva — e ciò che a conteneva era dove si trova la lista, non una copia della lista. Quindi entrambi i nomi ora puntano alla stessa lista. b.append(4) ha modificato quella singola lista, e entrambi i nomi mostrano la stessa modifica.
Con i numeri non lo vedi mai, perché b = b + 1 non modifica il vecchio numero — ne crea uno nuovo e lo mette in b. I numeri sono sicuri proprio perché non possono essere cambiati. Le liste possono, quindi non lo sono.
Se vuoi una copia separata, devi chiederla:
a = [1, 2, 3]
b = a.copy()
b.append(4)
print(a)
print(b)[1, 2, 3]
[1, 2, 3, 4]Quando fare attenzione. Ogni volta che metti una lista sotto un secondo nome, o passi una lista a una funzione (capitolo diciannove), chieditelo una volta: intendo cambiare questa, o voglio una copia? Chi non fa mai quella domanda passa giorni a cacciare bug che si leggono come "ma io non ho cambiato niente qui".
Un esempio completo
shopping.py:
# A shopping list, built up and then summarised
items = []
items.append("pen")
items.append("notebook")
items.append("bag")
items.insert(0, "eraser")
print("List :", items)
print("Count :", len(items))
print("First :", items[0])
print("Last :", items[-1])
print("Has bag :", "bag" in items)
items.remove("pen")
dropped = items.pop()
print()
print("After removing 'pen' and popping the last one:")
print("List :", items)
print("Dropped :", dropped)
print("Count :", len(items))
print()
print("Numbered:")
for position, item in enumerate(items, start=1):
print(f" {position}. {item}")List : ['eraser', 'pen', 'notebook', 'bag']
Count : 4
First : eraser
Last : bag
Has bag : True
After removing 'pen' and popping the last one:
List : ['eraser', 'notebook']
Dropped : bag
Count : 2
Numbered:
1. eraser
2. notebookTre cose che meritano attenzione.
Parte da una lista vuota. La riga items = [] fa lo stesso lavoro che faceva un accumulatore fuori dal ciclo nel capitolo dieci — dove c'era total = 0, ora c'è []. Ti serve un posto dove raccogliere prima di iniziare a raccogliere.
insert(0, "eraser") l'ha messo in testa, quindi eraser è il primo nella lista stampata. L'ordine in cui gli elementi sono stati aggiunti non è l'ordine in cui finiscono.
pop() ha restituito ciò che ha rimosso, quindi dropped contiene bag. remove("pen") non ha restituito niente, quindi il pen rimosso è semplicemente sparito.
Quando qualcosa va storto
IndexError: list index out of range Un indice oltre il numero di valori nella lista. In una lista di tre gli indici validi sono 0, 1, 2 — non esiste prices[3]. Per l'ultimo valore, prices[-1] è la cosa più sicura da scrivere, perché non richiede di conoscere la lunghezza.
ValueError: list.remove(x): x not in list Il valore che hai chiesto di rimuovere non c'è. Controlla prima con if x in stock:, oppure usa try dal capitolo ventiquattro.
AttributeError: 'NoneType' object has no attribute 'append' Una riga precedente diceva stock = stock.append(...), quindi stock ora è None. Non conservare il risultato — stock.append(...) da solo è l'intera operazione.
Ho modificato una lista e un'altra è cambiata anche lei Due nomi sono attaccati a una sola lista. Scrivi b = a.copy() invece di b = a.
Sto rimuovendo dentro un ciclo e alcuni elementi vengono saltati
numbers = [1, 2, 2, 3, 4]
for n in numbers:
if n % 2 == 0:
numbers.remove(n)
print(numbers)[1, 2, 3]Un 2 è sopravvissuto. Accorciare la lista che stai percorrendo mette il conteggio di Python fuori passo con le posizioni della lista, quindi alcuni valori vengono saltati. La soluzione non è rimuovere, ma raccogliere in una nuova lista ciò che vuoi tenere.
numbers = [1, 2, 2, 3, 4]
kept = []
for n in numbers:
if n % 2 != 0:
kept.append(n)
print(kept)[1, 3]Ho chiamato una variabile list e adesso list(...) non funziona list è uno dei nomi predefiniti di Python. Usane uno significativo — items, prices, names.
Passo 4 di 6 — Prevedi
Verifica cosa hai capito
Il risultato di append viene salvato di nuovo in stock. Cosa viene stampato?
stock = ["pen", "notebook"]
stock = stock.append("bag")
print(stock)- ANone
- B['pen', 'notebook', 'bag']
- C['bag']
- DUn `AttributeError`
È stata cambiata solo b. Cosa stampano le due righe?
a = [1, 2, 3]
b = a
b.append(4)
print(a)
print(b)- A[1, 2, 3] [4]
- B[1, 2, 3] [1, 2, 3, 4]
- C[1, 2, 3, 4] [1, 2, 3]
- D[1, 2, 3, 4] [1, 2, 3, 4]
La lista contiene tre valori e viene richiesto l'indice tre. Cosa succede?
prices = [15, 60, 850]
print(prices[3])- AUna lista vuota
- BUn `IndexError` — con tre valori gli indici validi sono `0`, `1`, `2`
- CNone
- D850 — l'ultimo valore
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 marks.py che contiene una lista di almeno sei voti.
Poi stampa:
- L'intera lista, e quanti voti contiene
- Il primo e l'ultimo voto — usa
[-1]per l'ultimo - Il totale e la media, accumulati con un ciclo anziché digitati a mano
- Una lista numerata usando
enumerate, che parte da uno - Una nuova lista con i voti sufficienti (40 o più), e quanti sono
Poi esegui due esperimenti, e entrambi contano:
- Aggiungi con
appendun altro voto alla fine e stampa di nuovo il totale. Hai dovuto cambiare la riga che lo calcola? Se no, hai scritto un calcolo vero. - Scrivi
backup = marks, poimarks.append(100), e stampa entrambi. Cosa c'è inbackup? Adesso fai la stessa cosa con.copy()e osserva la differenza.
Quell'ultimo esperimento vale i tre minuti più preziosi di questo capitolo. Vederlo una volta con i tuoi occhi significa non spendere mai più un pomeriggio dietro di esso.
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