Chapter 10 ListeQuesto capitolo illustra uno dei più utili tipi predefiniti di Python, le liste. Imparerete anche altri dettagli sugli oggetti, e vedrete cosa succede in presenza di uno stesso oggetto con più nomi. 10.1 Una lista è una sequenzaCome una stringa, una lista è una sequenza di valori. Mentre in una stringa i valori sono dei caratteri, in una lista possono essere di qualsiasi tipo. I valori che fanno parte della lista sono chiamati elementi. Ci sono parecchi modi di creare una nuova lista, e quello più semplice
è racchiudere i suoi elementi tra parentesi quadrate ( [10, 20, 30, 40] ['Primi piatti', 'Secondi piatti', 'Dessert'] Il primo esempio è una lista di quattro interi, il secondo una lista di tre stringhe. Gli elementi di una stessa lista non devono necessariamente essere tutti dello stesso tipo. La lista che segue contiene una stringa, un numero in virgola mobile, un intero e (meraviglia!) un’altra lista: ['spam', 2.0, 5, [10, 20]] Una lista all’interno di un’altra lista è detta lista nidificata. Una lista che non contiene elementi è detta lista vuota; potete crearne una scrivendo le due parentesi quadre vuote, Avrete già intuito che potete assegnare i valori della lista a variabili: >>> formaggi = ['Cheddar', 'Edam', 'Gouda'] >>> numeri = [42, 123] >>> vuota = [] >>> print(formaggi, numeri, vuota) ['Cheddar', 'Edam', 'Gouda'] [42, 123] [] 10.2 Le liste sono mutabiliLa sintassi per l’accesso agli elementi di una lista è la stessa che abbiamo già visto per i caratteri di una stringa: le parentesi quadre, con un’espressione tra parentesi che specifica l’indice dell’elemento (non dimenticate che gli indici partono da 0!): >>> formaggi[0] 'Cheddar' A differenza delle stringhe, le liste sono mutabili. Quando l’operatore parentesi quadre compare sul lato sinistro di un’assegnazione, identifica l’elemento della lista che sarà riassegnato: >>> numeri = [42, 123] >>> numeri[1] = 5 >>> numeri [42, 5] L’elemento di indice 1 di numeri, che era 123, ora è 5 La Figura 10.1 mostra il diagramma di formaggi, numeri e vuota: Le liste sono rappresentate da riquadri con la parola “list” all’esterno e i suoi elementi all’interno. formaggi si riferisce a una lista con tre elementi di indice 0, 1 e 2. numeri contiene due elementi; il diagramma mostra che il valore del secondo elemento è stato riassegnato da 123 a 5. vuota si riferisce a una lista senza elementi. Gli indici di una lista funzionano nello stesso modo già visto per le stringhe:
Anche l’operatore in funziona con le liste: >>> formaggi = ['Cheddar', 'Edam', 'Gouda'] >>> 'Edam' in formaggi True >>> 'Brie' in formaggi False 10.3 Attraversamento di una listaIl modo più frequente di attraversare gli elementi di una lista è un ciclo for. La sintassi è la stessa delle stringhe: for formaggio in formaggi: print(formaggio) Questo metodo funziona bene per leggere gli elementi di una lista, ma se volete scrivere o aggiornare degli elementi vi servono gli indici. Un modo per farlo è usare una combinazione delle funzioni predefinite range e len: for i in range(len(numeri)): numeri[i] = numeri[i] * 2 Questo ciclo attraversa la lista e aggiorna tutti gli elementi. len restituisce il numero di elementi della lista. range restituisce una lista di indici da 0 a n−1, dove n è la lunghezza della lista. Ad ogni ripetizione del ciclo, i prende l’indice dell’elemento successivo. L’istruzione di assegnazione nel corpo usa i per leggere il vecchio valore dell’elemento e assegnare quello nuovo. Un ciclo for su una lista vuota non esegue mai il corpo: for x in []: print('Questo non succede mai.') Sebbene una lista possa contenerne un’altra, quella nidificata conta sempre come un singolo elemento. Quindi la lunghezza di questa lista è quattro: ['spam', 1, ['Brie', 'Roquefort', 'Pol le Veq'], [1, 2, 3]] 10.4 Operazioni sulle listeL’operatore + concatena delle liste: >>> a = [1, 2, 3] >>> b = [4, 5, 6] >>> c = a + b >>> c [1, 2, 3, 4, 5, 6] L’operatore * ripete una lista per un dato numero di volte: >>> [0] * 4 [0, 0, 0, 0] >>> [1, 2, 3] * 3 [1, 2, 3, 1, 2, 3, 1, 2, 3] Il primo esempio ripete [0] per quattro volte. Il secondo ripete la lista [1, 2, 3] per tre volte. 10.5 Slicing delle listeAnche l’operazione di slicing funziona sulle liste: >>> t = ['a', 'b', 'c', 'd', 'e', 'f'] >>> t[1:3] ['b', 'c'] >>> t[:4] ['a', 'b', 'c', 'd'] >>> t[3:] ['d', 'e', 'f'] Se omettete il primo indice, lo slicing comincia dall’inizio, mentre se manca il secondo, termina alla fine. Se vengono omessi entrambi, lo slicing è una copia dell’intera lista. >>> t[:] ['a', 'b', 'c', 'd', 'e', 'f'] Dato che le liste sono mutabili, spesso è utile farne una copia prima di eseguire operazioni che le modificano. Un operatore di slicing sul lato sinistro di un’assegnazione, permette di aggiornare più elementi. >>> t = ['a', 'b', 'c', 'd', 'e', 'f'] >>> t[1:3] = ['x', 'y'] >>> t ['a', 'x', 'y', 'd', 'e', 'f'] 10.6 Metodi delle listePython fornisce dei metodi che operano sulle liste. Ad esempio, append aggiunge un nuovo elemento in coda alla lista: >>> t = ['a', 'b', 'c'] >>> t.append('d') >>> t ['a', 'b', 'c', 'd'] extend prende una lista come argomento e accoda tutti i suoi elementi: >>> t1 = ['a', 'b', 'c'] >>> t2 = ['d', 'e'] >>> t1.extend(t2) >>> t1 ['a', 'b', 'c', 'd', 'e'] Questo esempio lascia immutata la lista t2. sort dispone gli elementi della lista in ordine crescente: >>> t = ['d', 'c', 'e', 'b', 'a'] >>> t.sort() >>> t ['a', 'b', 'c', 'd', 'e'] La maggior parte dei metodi delle liste sono vuoti: modificano la lista e restituiscono None. Se scrivete accidentalmente t = t.sort(), il risultato vi deluderà. 10.7 Mappare, filtrare e ridurrePer sommare tutti i numeri in una lista, potete usare un ciclo come questo: def somma_tutti(t): totale = 0 for x in t: totale += x return totale totale è inizializzato a 0. Ad ogni ripetizione del ciclo, x prende un elemento dalla lista. L’operatore += è una forma abbreviata per aggiornare una variabile. Questa istruzione di assegnazione potenziata, totale += x è equivalente a totale = totale + x Man mano che il ciclo lavora, totale accumula la somma degli elementi; una variabile usata in questo modo è detta anche accumulatore. Sommare gli elementi di una lista è un’operazione talmente comune che Python contiene una apposita funzione predefinita, sum: >>> t = [1, 2, 3] >>> sum(t) 6 Una simile operazione che compatta una sequenza di elementi in un singolo valore, è chiamata riduzione. Talvolta è necessario attraversare una lista per costruirne contemporaneamente un’altra. Per esempio, la funzione seguente prende una lista di stringhe e restituisce una nuova lista che contiene le stesse stringhe in lettere maiuscole: def tutte_maiuscole(t): res = [] for s in t: res.append(s.capitalize()) return res res è inizializzata come una lista vuota; ad ogni ripetizione del ciclo viene accodato un elemento. Pertanto res è una sorta di accumulatore. Un’operazione come quella di Un’altra operazione frequente è la selezione di alcuni elementi di una lista per formare una sottolista. Per esempio, la seguente funzione prende una lista di stringhe e restituisce una lista che contiene solo le stringhe scritte in lettere maiuscole: def solo_maiuscole(t): res = [] for s in t: if s.isupper(): res.append(s) return res isupper è un metodo delle stringhe che restituisce True se la stringa contiene solo lettere maiuscole. Un’operazione come quella di La maggior parte delle operazioni sulle liste possono essere espresse come combinazioni di mappa, filtro e riduzione. 10.8 Cancellare elementiCi sono alcuni modi per cancellare elementi da una lista. Se conoscete l’indice dell’elemento desiderato, potete usare pop: >>> t = ['a', 'b', 'c'] >>> x = t.pop(1) >>> t ['a', 'c'] >>> x 'b' pop modifica la lista e restituisce l’elemento che è stato rimosso. Se omettete l’indice, il metodo cancella e restituisce l’ultimo elemento della lista. Se non vi serve il valore rimosso, potete usare l’operatore del: >>> t = ['a', 'b', 'c'] >>> del t[1] >>> t ['a', 'c'] Se conoscete l’elemento da rimuovere ma non il suo indice, potete usare remove: >>> t = ['a', 'b', 'c'] >>> t.remove('b') >>> t ['a', 'c'] Il valore di ritorno di remove è None. Per cancellare più di un elemento potete usare del con lo slicing: >>> t = ['a', 'b', 'c', 'd', 'e', 'f'] >>> del t[1:5] >>> t ['a', 'f'] Come di consueto, lo slicing seleziona gli elementi fino al secondo indice escluso. 10.9 Liste e stringheUna stringa è una sequenza di caratteri e una lista è una sequenza di valori, ma una lista di caratteri non è la stessa cosa di una stringa. Per convertire una stringa in una lista di caratteri, potete usare list: >>> s = 'spam' >>> t = list(s) >>> t ['s', 'p', 'a', 'm'] Poiché list è una funzione predefinita, va evitato di chiamare una variabile con questo nome. Personalmente evito anche l perché somiglia troppo a 1. Ecco perché di solito uso t. La funzione list separa una stringa in singole lettere. Se invece volete spezzare una stringa nelle singole parole, usate il metodo split: >>> s = 'profonda nostalgia dei fiordi' >>> t = s.split() >>> t ['profonda', 'nostalgia', 'dei', 'fiordi'] Un argomento opzionale chiamato delimitatore specifica quale carattere va considerato come separatore delle parole. L’esempio che segue usa il trattino come separatore: >>> s = 'spam-spam-spam' >>> delimita = '-' >>> t = s.split(delimita) >>> t ['spam', 'spam', 'spam'] join è l’inverso di split: prende una lista di stringhe e concatena gli elementi. join è un metodo delle stringhe, quindi lo dovete invocare per mezzo del delimitatore e passare la lista come parametro: >>> t = ['profonda', 'nostalgia', 'dei', 'fiordi'] >>> delimita = ' ' >>> s = delimita.join(t) >>> s 'profonda nostalgia dei fiordi' In questo caso il delimitatore è uno spazio, quindi
join aggiunge uno spazio tra le parole. Per concatenare delle stringhe senza spazi, basta usare come delimitatore la stringa vuota 10.10 Oggetti e valoriSe eseguiamo queste istruzioni di assegnazione: a = 'banana' b = 'banana' Sappiamo che a e b si riferiscono a una stringa, ma non sappiamo se si riferiscono alla stessa stringa. Ci sono due possibili stati, illustrati in Figura 10.2. In un caso, a e b si riferiscono a due oggetti diversi che hanno lo stesso valore. Nell’altro, si riferiscono allo stesso oggetto. Per controllare se due variabili si riferiscono allo stesso oggetto, potete usare l’operatore is. >>> a = 'banana' >>> b = 'banana' >>> a is b True In questo esempio, Python ha creato un unico oggetto stringa, e sia a che b fanno riferimento ad esso. Ma se create due liste, ottenete due oggetti distinti: >>> a = [1, 2, 3] >>> b = [1, 2, 3] >>> a is b False Quindi il diagramma di stato somiglia a quello di Figura 10.3. In quest’ultimo caso si dice che le due liste sono equivalenti, perché contengono gli stessi elementi, ma non identiche, perché non sono lo stesso oggetto. Se due oggetti sono identici, sono anche equivalenti, ma se sono equivalenti non sono necessariamente identici. Fino ad ora abbiamo usato “oggetto” e “valore” indifferentemente, ma è più preciso dire che un oggetto ha un valore. Se valutate [1,2,3], ottenete un oggetto lista il cui valore è una sequenza di interi. Se un’altra lista contiene gli stessi elementi, diciamo che ha lo stesso valore, ma non che è lo stesso oggetto. 10.11 AliasSe a si riferisce a un oggetto e assegnate b = a, allora entrambe le variabili si riferiscono allo stesso oggetto. >>> a = [1, 2, 3] >>> b = a >>> b is a True Il diagramma di stato è quello in Figura 10.4. L’associazione tra una variabile e un oggetto è chiamato riferimento. In questo esempio ci sono due riferimenti allo stesso oggetto. Un oggetto che ha più di un riferimento ha anche più di un nome, e si dice quindi che l’oggetto ha degli alias. Se l’oggetto munito di alias è mutabile, i cambiamenti provocati da un alias si riflettono anche sull’altro: >>> b[0] = 42 >>> a [42, 2, 3] Sebbene questo comportamento possa essere utile, è anche fonte di errori. In genere è più sicuro evitare gli alias quando si sta lavorando con oggetti mutabili. Per gli oggetti immutabili come le stringhe, gli alias non sono un problema. In questo esempio: a = 'banana' b = 'banana' Non fa quasi mai differenza se a e b facciano riferimento alla stessa stringa o meno. 10.12 Liste come argomentiQuando passate una lista a una funzione, questa riceve un riferimento alla lista. Se la funzione modifica la lista, il chiamante vede la modifica. Per esempio, def decapita(t): del t[0] Vediamo come si usa: >>> lettere = ['a', 'b', 'c'] >>> decapita(lettere) >>> lettere ['b', 'c'] Il parametro t e la variabile lettere sono due alias dello stesso oggetto. Il diagramma di stack è riportato in Figura 10.5. Dato che la lista è condivisa da due frame, la disegno in mezzo. È importante distinguere tra operazioni che modificano le liste e operazioni che creano nuove liste. Per esempio il metodo append modifica una lista, ma l’operatore + ne crea una nuova. Ecco un esempio che usa append: >>> t1 = [1, 2] >>> t2 = t1.append(3) >>> t1 [1, 2, 3] >>> t2 None Il valore di ritorno di append è None. Un esempio di utilizzo dell’operatore +: >>> t3 = t1 + [4] >>> t1 [1, 2, 3] >>> t3 [1, 2, 3, 4] Il risultato è una nuova lista, e la lista di origine resta immutata. Questa differenza è importante quando scrivete delle funzioni che devono modificare delle liste. Per esempio, questa funzione non cancella il primo elemento della lista: def non_decapita(t): t = t[1:] # SBAGLIATO! L’operatore di slicing crea una nuova lista e l’assegnazione fa in modo che t si riferisca ad essa, ma tutto ciò non ha effetti sul chiamante. >>> t4 = [1, 2, 3] >>> non_decapita(t4) >>> t4 [1, 2, 3] Alla chiamata di Un’alternativa valida è scrivere una funzione che crea e restituisce una nuova lista. Per esempio, ritaglia restituisce tutti gli elementi di una lista tranne il primo: def ritaglia(t): return t[1:] Questa funzione lascia intatta la lista di origine. Ecco come si usa: >>> lettere = ['a', 'b', 'c'] >>> resto = ritaglia(lettere) >>> resto ['b', 'c'] 10.13 DebugUn uso poco accurato delle liste (e degli altri oggetti mutabili) può portare a lunghe ore di debug. Ecco alcune delle trappole più comuni e i modi per evitarle:
10.14 Glossario
10.15 EserciziPotete scaricare le soluzioni degli esercizi seguenti all’indirizzo http://thinkpython2.com/code/list_exercises.py.
Esercizio 1 Scrivete una funzione di nome >>> t = [[1, 2], [3], [4, 5, 6]] >>> somma_nidificata(t) 21
Esercizio 2
Scrivete una funzione di nome >>> t = [1, 2, 3] >>> somma_cumulata(t) [1, 3, 6]
Esercizio 3 Scrivete una funzione di nome >>> t = [1, 2, 3, 4] >>> mediani(t) [2, 3]
Esercizio 4 Scrivete una funzione di nome >>> t = [1, 2, 3, 4] >>> tronca(t) >>> t [2, 3]
Esercizio 5
Scrivete una funzione di nome ordinata che prenda una lista come parametro e restituisca True se la lista è ordinata in senso crescente, False altrimenti. Esempio:>>> ordinata([1, 2, 2]) True >>> ordinata(['b', 'a']) False
Esercizio 6
Due parole sono anagrammi se potete ottenerle riordinando le lettere di cui sono composte. Scrivete una funzione di nome
Esercizio 7
Scrivete una funzione di nome
Esercizio 8 Questo è un esercizio sul cosiddetto “Paradosso del compleanno”; potete approfondirlo leggendo http://it.wikipedia.org/wiki/Paradosso_del_compleanno. Se in una classe ci sono 23 studenti, quante probabilità ci sono che due di loro compiano gli anni lo stesso giorno? Potete stimare questa probabilità generando alcuni campioni a caso di 23 date e controllando le corrispondenze. Suggerimento: per generare date in modo casuale usate la funzione randint nel modulo random. Potete scaricare la mia soluzione da http://thinkpython2.com/code/birthday.py.
Esercizio 9
Scrivete una funzione che legga il file words.txt e crei una lista in cui ogni parola è un elemento. Scrivete due versioni della funzione, una che usi il metodo append e una il costrutto t = t + [x]. Quale richiede più tempo di esecuzione? Perché? Soluzione: http://thinkpython2.com/code/wordlist.py.
Esercizio 10
Per controllare se una parola è contenuta in un elenco, è possibile usare l’operatore in, ma è un metodo lento, perché ricerca le parole seguendo il loro ordine. Dato che le parole sono in ordine alfabetico, possiamo accelerare l’operazione con una ricerca binaria (o per bisezione), che è un po’ come cercare una parola nel vocabolario. Partite nel mezzo e controllate se la parola che cercate viene prima o dopo la parola di metà elenco. Se prima, cercherete nella prima metà nello stesso modo, se dopo, cercherete nella seconda metà. Ad ogni passaggio, dimezzate lo spazio di ricerca. Se l’elenco ha 113.809 parole, ci vorranno circa 17 passaggi per trovare la parola o concludere che non c’è. Scrivete una funzione di nome bisezione che richieda una lista ordinata e un valore da ricercare, e restituisca l’indice di quel valore, se fa parte della lista, oppure None se non esiste. Oppure, potete leggere la documentazione del modulo bisect e usare quello! Soluzione: http://thinkpython2.com/code/inlist.py.
Esercizio 11
Una coppia di parole è “bifronte” se l’una si legge nel verso opposto dell’altra. Scrivete un programma che trovi tutte le parole bifronti nella lista di parole. Soluzione: http://thinkpython2.com/code/reverse_pair.py.
Esercizio 12
Due parole si “incastrano” se, prendendo le loro lettere alternativamente dall’una e dall’altra, si forma una nuova parola. Per esempio, le parole inglesi “shoe” and “cold” incastrandosi formano “schooled”.
Soluzione: http://thinkpython2.com/code/interlock.py. Fonte: Questo esercizio è tratto da un esempio di http://puzzlers.org. |
ContributeIf you would like to make a contribution to support my books, you can use the button below. Thank you!
Are you using one of our books in a class?We'd like to know about it. Please consider filling out this short survey.
|