Next Up Previous Hi Index

Chapter 8

Liste

Una lista è una serie ordinata di valori, ognuno identificato da un indice. I valori che fanno parte della lista sono chiamati elementi. Le liste sono simili alle stringhe essendo insiemi ordinati di caratteri, fatta eccezione per il fatto che gli elementi di una lista possono essere di tipo qualsiasi. Liste e stringhe (e altri tipi di dati che si comportano da insiemi ordinati) sono chiamate sequenze.

8.1 Valori della lista

Ci sono parecchi modi di creare una lista nuova, e quello più semplice è racchiudere i suoi elementi tra parentesi quadrate ([ e ]):

[10, 20, 30, 40]
["Pippo", "Pluto", "Paperino"]

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. \ Questa lista contiene una stringa, un numero in virgola mobile, un intero ed un'altra lista:

["ciao", 2.0, 5, [10, 20]]

Una lista all'interno di un'altra lista è detta lista annidata.

Le liste che contengono numeri interi consecutivi sono così comuni che Python fornisce un modo semplice per crearle:

>>> range(1,5)
[1, 2, 3, 4]

La funzione range prende due argomenti e ritorna una lista che contiene tutti gli interi a partire dal primo (incluso) fino al secondo (escluso).

Ci sono altre due forme per range. Con un solo argomento crea una lista a partire da 0:

>>> range(10)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

Se è presente un terzo argomento questo specifica l'intervallo tra valori successivi, chiamato passo. Questo esempio mostra come ottenere una stringa dei numeri dispari tra 1 e 10:

>>> range(1, 10, 2)
[1, 3, 5, 7, 9]

Infine esiste una lista speciale che non contiene alcun elemento: è chiamata lista vuota ed è indicata da [].

Con tutti questi modi di creare liste sarebbe un peccato non poter variare il contenuto di una lista o poter passare liste come parametri di funzioni. Infatti entrambe queste cose possono essere fatte:

>>> Vocabolario = ["amico", "casa", "telefono"]
>>> Numeri = [17, 123]
>>> ListaVuota = []
>>> print Vocabolario, Numeri, ListaVuota
['amico', 'casa', 'telefono'] [17, 123] []

8.2 Accesso agli elementi di una lista

La sintassi per l'accesso agli elementi di una lista è la stessa che abbiamo già visto per i caratteri di una stringa: anche in questo caso facciamo uso dell'operatore porzione ([]). L'espressione tra parentesi quadrate specifica l'indice dell'elemento (non dimenticare che gli indici partono da 0!):

>>> print Numeri[0]
17
>>> Numeri[1] = 5

L'operatore porzione può comparire in qualsiasi posto di un'espressione: quando è alla sinistra di un'assegnazione cambia uno degli elementi della lista (nell'esempio appena visto l'elemento 123 è diventato 5).

Come indice possiamo inoltre usare qualsiasi espressione che produca un intero:

>>> Numeri[3-2]
5
>>> Numeri[1.0]
TypeError: sequence index must be integer

Provando a leggere o modificare un elemento che non esiste si ottiene un messaggio d'errore:

>>> Numeri[2] = 5
IndexError: list assignment index out of range

Se un indice ha valore negativo il conteggio parte dalla fine della lista:

>>> Numeri[-1]
5
>>> Numeri[-2]
17
>>> Numeri[-3]
IndexError: list index out of range

Numeri[-1] è quindi l'ultimo elemento della lista, Numeri[-2] il penultimo e Numeri[-3] non esiste essendo la nostra lista composta di 2 soli elementi.

È comune usare una variabile di ciclo come indice di una lista:

Squadre = ["Juventus", "Inter", "Milan", "Roma"]

i = 0
while i < 4:
  print Squadre[i]
  i = i + 1

Questo ciclo while conta da 0 a 4: quando l'indice del ciclo i vale 4 la condizione diventa falsa e il ciclo termina. Il corpo del ciclo è eseguito solo quando i è 0, 1, 2 e 3.

Ad ogni ciclo la variabile i è usata come indice della lista: questo tipo di elaborazione è chiamata elaborazione trasversale di una lista o attraversamento di una lista.

8.3 Lunghezza di una lista

La funzione len ritorna la lunghezza di una lista. È sempre bene usare len per conoscere il limite superiore in un ciclo, piuttosto che usare un valore costante: in questo modo se la lunghezza della lista dovesse cambiare non dovrai scorrere il programma per modificarne i cicli, e sicuramente len funzionerà correttamente per liste di ogni lunghezza:

Squadre = ["Juventus", "Inter", "Milan", "Roma"]

i = 0
while i < len(Squadre):
  print Squadre[i]
  i = i + 1

L'ultima volta che il ciclo è eseguito i vale len(Squadre) - 1 che è l'indice dell'ultimo elemento della lista. Quando al successivo incremento i diventa len(Squadre) la condizione diventa falsa ed il corpo non è eseguito, dato che len(Squadre) non è un indice valido.

Sebbene una lista possa contenere a sua volta un'altra lista questa lista annidata conta come un singolo elemento indipendentemente dalla sua lunghezza. La lunghezza della lista seguente è 4:

['ciao!', 1, ['mela', 'pera', 'banana'], [1, 2, 3]]

Esercizio: scrivi un ciclo che attraversa la lista precedente e stampa la lunghezza di ogni elemento.

8.4 Appartenenza ad una lista

in è un operatore booleano (restituisce vero o falso) che controlla se un valore è presente in una lista. L'abbiamo già usato con le stringhe nella sezione 7.10 ma funziona anche con le liste e con altri tipi di sequenze:

>>> Squadre = ['Juventus', 'Inter', 'Milan', 'Roma']
>>> 'Inter' in Squadre
1
>>> 'Arsiero' in Squadre
0

Dato che Inter è un membro della lista Squadre l'operatore in ritorna vero; Arsiero non fa parte della lista e l'operazione in ritorna falso.

Possiamo usare not in combinazione con in per controllare se un elemento non fa parte di una lista:

>>> 'Arsiero' not in Squadre
1

8.5 Liste e cicli for

Il ciclo for che abbiamo visto nella sezione 7.3 funziona anche con le liste. La sintassi generica per il ciclo for in questo caso è:

for VARIABILE in LISTA:
  CORPO

Questa istruzione è equivalente a:

i = 0
while i < len(LISTA):
  VARIABILE = LISTA[i]
  CORPO
  i = i + 1

Il ciclo for è più conciso perché possiamo eliminare l'indice del ciclo i. Ecco il ciclo di uno degli esempi appena visti riscritto con il for:

for Squadra in Squadre:
  print Squadra

Si legge quasi letteralmente: "Per (ciascuna) Squadra in (nella lista di) Squadre, stampa (il nome della) Squadra".

Nel ciclo for può essere usata qualsiasi espressione che produca una lista:

for Numero in range(20):
  if Numero % 2 == 0:
    print Numero

for Frutto in ["banana", "mela", "pera"]:
  print "Mi piace la" + Frutto + "!"

Il primo esempio stampa tutti i numeri pari tra 0 e 19. Il secondo esprime l'entusiasmo per la frutta.

8.6 Operazioni sulle liste

L'operatore + concatena le liste:

>>> a = [1, 2, 3]
>>> b = [4, 5, 6]
>>> c = a + b
>>> print c
[1, 2, 3, 4, 5, 6]

L'operatore * ripete una lista un dato numero di volte:

>>> [0] * 4
[0, 0, 0, 0]
>>> [1, 2, 3] * 3
[1, 2, 3, 1, 2, 3, 1, 2, 3]

Nel primo esempio abbiamo ripetuto [0] quattro volte. Nel secondo abbiamo ripetuto la lista [1, 2, 3] tre volte.

8.7 Porzioni di liste

Le porzioni che abbiamo già visto alla sezione 7.4 lavorano anche con le liste:

>>> Lista = ['a', 'b', 'c', 'd', 'e', 'f']
>>> Lista[1:3]
['b', 'c']
>>> Lista[:4]
['a', 'b', 'c', 'd']
>>> Lista[3:]
['d', 'e', 'f']
>>> Lista[:]
['a', 'b', 'c', 'd', 'e', 'f']

8.8 Le liste sono mutabili

A differenza delle stringhe le liste sono mutabili e ciò significa che gli elementi possono essere modificati. Usando l'operatore porzione nella parte sinistra dell'assegnazione possiamo aggiornare un elemento:

>>> Frutta = ["banana", "mela", "susina"]
>>> Frutta[0] = "pera"
>>> Frutta[-1] = "arancia"
>>> print Frutta
['pera', 'mela', 'arancia']

Con l'operatore porzione possiamo modificare più elementi alla volta:

>>> Lista = ['a', 'b', 'c', 'd', 'e', 'f']
>>> Lista[1:3] = ['x', 'y']
>>> print Lista
['a', 'x', 'y', 'd', 'e', 'f']

Possiamo rimuovere elementi da una lista assegnando loro una lista vuota:

>>> Lista = ['a', 'b', 'c', 'd', 'e', 'f']
>>> Lista[1:3] = []
>>> print Lista
['a', 'd', 'e', 'f']

Possono essere aggiunti elementi ad una lista inserendoli in una porzione vuota nella posizione desiderata:

>>> Lista = ['a', 'd', 'f']
>>> Lista[1:1] = ['b', 'c']
>>> print Lista
['a', 'b', 'c', 'd', 'f']
>>> Lista[4:4] = ['e']
>>> print Lista
['a', 'b', 'c', 'd', 'e', 'f']

8.9 Cancellazione di liste

Usare le porzioni per cancellare elementi delle liste non è poi così pratico ed è facile sbagliare. Python fornisce un'alternativa molto più leggibile.

del rimuove un elemento da una lista:

>>> a = ['uno', 'due', 'tre']
>>> del a[1]
>>> a
['uno', 'tre']

Come puoi facilmente immaginare del gestisce anche gli indici negativi e avvisa con messaggio d'errore se l'indice è al di fuori dei limiti ammessi.

Puoi usare una porzione come indice di del:

>>> Lista = ['a', 'b', 'c', 'd', 'e', 'f']
>>> del Lista[1:5]
>>> print Lista
['a', 'f']

Come abbiamo già visto la porzione indica tutti gli elementi a partire dal primo indice incluso fino al secondo indice escluso.

8.10 Oggetti e valori

Se eseguiamo queste istruzioni

a = "banana"
b = "banana"

sappiamo che sia a che b si riferiscono ad una stringa contenente le lettere "banana". A prima vista non possiamo dire se puntano alla stessa stringa in memoria.

I possibili casi sono due:

Nel primo caso a e b si riferiscono a due diverse "cose" che hanno lo stesso valore. Nel secondo caso si riferiscono alla stessa "cosa". Queste "cose" hanno un nome: oggetti. Un oggetto è un qualcosa cui può far riferimento una variabile.

Ogni oggetto ha un identificatore unico che possiamo ricavare con la funzione id. Stampando l'identificatore di a e di b possiamo dire subito se le due variabili si riferiscono allo stesso oggetto:

>>> id(a)
135044008
>>> id(b)
135044008

Otteniamo lo stesso identificatore e ciò significa che Python ha creato in memoria un'unica stringa cui fanno riferimento entrambe le variabili a e b.

In questo ambito le liste si comportano diversamente dalle stringhe, dato che quando creiamo due liste queste sono sempre oggetti diversi:

>>> a = [1, 2, 3]
>>> b = [1, 2, 3]
>>> id(a)
135045528
>>> id(b)
135041704

Il diagramma di stato in questo caso è

a e b hanno lo stesso valore ma non si riferiscono allo stesso oggetto.

8.11 Alias

Dato che le variabili si riferiscono ad oggetti quando assegniamo una variabile ad un'altra entrambe le variabili si riferiscono allo stesso oggetto:

>>> a = [1, 2, 3]
>>> b = a

In questo caso il diagramma di stato è

La stessa lista in questo caso ha due nomi differenti, a e b, e diciamo che questi sono due alias. Dato che l'oggetto cui entrambi si riferiscono è lo stesso è indifferente quale degli alias si usi per effettuare un'elaborazione:

>>> b[0] = 5
>>> print a
[5, 2, 3]

Sebbene questo comportamento possa essere desiderabile è nella maggior parte dei casi difficilmente controllabile e può portare a effetti indesiderati e inattesi. In generale è buona norma evitare gli alias in caso di oggetti mutabili, mentre per quelli immutabili non ci sono problemi. Ecco perché Python si permette di usare la stessa stringa con diversi alias quando si tratta di risparmiare memoria senza che questo fatto causi alcun problema. La stringa è un oggetto immutabile e quindi non può essere modificata: non c'è quindi il rischio di causare spiacevoli effetti collaterali.

8.12 Clonare le liste

Se vogliamo modificare una lista e mantenere una copia dell'originale dobbiamo essere in grado di copiare il contenuto della lista e non solo di creare un suo alias. Questo processo è talvolta chiamato clonazione per evitare l'ambiguità insita nella parola "copia".

Il modo più semplice per clonare una lista è quello di usare l'operatore porzione:

>>> a = [1, 2, 3]
>>> b = a[:]
>>> print b
[1, 2, 3]

Il fatto di prendere una porzione di a crea una nuova lista. In questo caso la porzione consiste degli elementi dell'intera lista, dato che non sono stati specificati gli indici iniziale e finale.

Ora siamo liberi di modificare b senza doverci preoccupare di a:

>>> b[0] = 5
>>> print a
[1, 2, 3]

Esercizio: disegna un diagramma di stato per a e per b prima e dopo questa modifica.

8.13 Parametri di tipo lista

Se passiamo una lista come parametro di funzione in realtà passiamo un suo riferimento e non una sua copia. Per esempio la funzione Testa prende una lista come parametro e ne ritorna il primo elemento:

def Testa(Lista):
  return Lista[0]

Ecco com'è usata:

>>> Numeri = [1, 2, 3]
>>> Testa(Numeri)
1

Il parametro Lista e la variabile Numeri sono alias dello stesso oggetto. Il loro diagramma di stato è

Dato che l'oggetto lista è condiviso da due frame l'abbiamo disegnato a cavallo di entrambi.

Se una funzione modifica una lista passata come parametro, viene modificata la lista stessa e non una sua copia. Per esempio CancellaTesta rimuove il primo elemento da una lista:

def CancellaTesta(Lista):
  del Lista[0]

Ecco com'è usata CancellaTesta:

>>> Numeri = [1, 2, 3]
>>> CancellaTesta(Numeri)
>>> print Numeri
[2, 3]

Quando una funzione ritorna una lista in realtà viene ritornato un riferimento alla lista stessa. Per esempio Coda ritorna una lista che contiene tutti gli elementi di una lista a parte il primo:

def Coda(Lista):
  return Lista[1:]

Ecco com'è usata Coda:

>>> Numeri = [1, 2, 3]
>>> Resto = Coda(Numeri)
>>> print Resto
[2, 3]

Dato che il valore ritornato è stato creato con l'operatore porzione stiamo restituendo una nuova lista. La creazione di Resto ed ogni suo successivo cambiamento non ha alcun effetto sulla lista originale Numeri.

8.14 Liste annidate

Una lista annidata è una lista che compare come elemento di un'altra lista. Nell'esempio seguente il quarto elemento della lista (ricorda che stiamo parlando dell'elemento numero 3 dato che il primo ha indice 0) è una lista:

>>> Lista = ["ciao", 2.0, 5, [10, 20]]

Se stampiamo Lista[3] otteniamo [10, 20]. Per estrarre un elemento da una lista annidata possiamo procedere in due tempi:

>>> Elemento = Lista[3]
>>> Elemento[0]
10

O possiamo combinare i due passi in un'unica istruzione:

>>> Lista[3][0]
10

L'operatore porzione viene valutato da sinistra verso destra così questa espressione ricava il quarto elemento (indice 3) di Lista ed essendo questo una lista ne estrae il primo elemento (indice 0).

8.15 Matrici

Le liste annidate sono spesso usate per rappresentare matrici. Per esempio la matrice

può essere rappresentata come

>>> Matrice = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]

Matrice è una lista di tre elementi dove ciascuno è una riga della matrice. Possiamo selezionare una singola riga nel solito modo:

>>> Matrice[1]
[4, 5, 6]

O estrarre una singola cella usando il doppio indice:

>>> Matrice[1][1]
5

Il primo indice seleziona la riga ed il secondo la colonna. Questo è un modo comune di rappresentare le matrici ma non è l'unico: una variante è quella di usare una lista di colonne invece che di righe. Vedremo in seguito un'alternativa completamente diversa quando avremo visto i dizionari.

8.16 Stringhe e liste

Due delle funzioni più utili nel modulo string hanno a che fare con le liste di stringhe. La funzione split spezza una stringa in una lista di parole singole, considerando un qualsiasi carattere di spazio bianco come punto di interruzione tra parole consecutive:

>>> import string
>>> Verso = "Nel mezzo del cammin..."
>>> string.split(Verso)
['Nel', 'mezzo', 'del', 'cammin...']

Può anche essere usato un argomento opzionale per specificare quale debba essere il delimitatore da considerare. In questo esempio usiamo la stringa el come delimitatore:

>>> string.split(Verso, 'el')
['N', ' mezzo d', ' cammin...']

Il delimitatore non appare nella lista.

La funzione join si comporta in modo inverso rispetto a split: prende una lista di stringhe e ne concatena gli elementi inserendo uno spazio tra ogni coppia:

>>> Lista = ['Nel', 'mezzo', 'del', 'cammin...']
>>> string.join(Lista)
'Nel mezzo del cammin...'

Come nel caso di split, join accetta un argomento opzionale che rappresenta il delimitatore da inserire tra gli elementi. Il delimitatore di default è uno spazio ma può essere cambiato:

>>> string.join(Lista, '_')
'Nel_mezzo_del_cammin...'

Esercizio: descrivi la relazione tra la lista Verso e cosa ottieni da string.join(string.split(Verso)). Sono le stesse per tutte le stringhe o in qualche caso possono essere diverse?

8.17 Glossario

Lista
collezione di oggetti identificata da un nome dove ogni oggetto è selezionabile grazie ad un indice.
Indice
variabile intera o valore che indica un elemento all'interno di una lista.
Elemento
valore in una lista (o in altri tipi di sequenza). L'operatore porzione seleziona gli elementi di una lista.
Sequenza
ognuno dei tipi di dati che consiste in una lista ordinata di elementi identificati da un indice.
Lista annidata
lista che è un elemento di un'altra lista.
Attraversamento di una lista
accesso in sequenza di tutti gli elementi di una lista.
Oggetto
zona di memoria cui si può riferire una variabile.
Alias
più variabili che si riferiscono allo stesso oggetto con nomi diversi.
Clonare
creare un nuovo oggetto che ha lo stesso valore di un oggetto già esistente.
Delimitatore
carattere o stringa usati per indicare dove una stringa deve essere spezzata.


Next Up Previous Hi Index