Slicing per le Liste in Python

In questa lezione introdurremo un'importante tecnica per accedere e modificare gli elementi di una lista in Python: lo slicing.

A differenza dell'indirizzamento normale di un elemento di una lista, attraverso lo slicing è possibile ottenere una nuova lista che contiene gli elementi di un intervallo di indici specificato. Infatti slicing vuol dire affettare in inglese.

Lo slicing è anche molto importante nel caso in cui volessimo modificare il contenuto di una lista. Ci permette, infatti, di modificare in un colpo solo più elementi di una lista.

Slicing di Liste

Nelle lezioni precedenti abbiamo visto che per accedere ad un elemento di una lista sia in lettura che in scrittura è sufficiente specificare l'indice dell'elemento. Ad esempio, per accedere al primo elemento di una lista l si può scrivere:

elemento = l[0]

Tuttavia, l'operazione di indicizzazione restituisce un unico elemento. Per poter accedere ad un sottoinsieme di elementi è necessario utilizzare la tecnica dello slicing, dall'inglese affettamento.

In pratica, lo slicing consiste nel specificare un intervallo di indici, che corrispondono agli elementi che si vogliono accedere. In particolare, bisogna specificare il primo indice del sotto-insieme e l'ultimo indice del sotto-insieme.

Il risultato dell'operazione di slicing è una nuova lista che contiene gli elementi dell'intervallo specificato. Bisogna notare però che la nuova lista contiene gli elementi dell'intervallo specificato escluso l'ultimo.

Chiariamo con un esempio:

l = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j']

Se vogliamo accedere agli elementi con indice compreso tra 2 e 5 (elemento con indice 5 escluso), dobbiamo specificare l'intervallo usando la seguente sintassi:

intervallo = l[2:5]

Il risultato sarà una nuova lista che contiene gli elementi con indice 2, 3 e 4. In particolare, la nuova lista sarà:

['c', 'd', 'e']

Come si può notare, l'elemento con indice 5, ossia f, non è stato incluso nella nuova lista.

Definizione

Slicing di una lista

Lo slicing di una lista è un'operazione che consente di ottenere una nuova lista a partire da una esistente, che contiene gli elementi di un intervallo di indici specificato.

La sintassi per effettuare lo slicing di una lista è la seguente:

nuova_lista = lista[indice_iniziale:indice_finale]

Il risultato di questa operazione è una nuova lista che contiene gli elementi della lista specificata, che hanno indice compreso tra indice_iniziale e indice_finale (escluso l'ultimo).

Slicing con indici impliciti

Come si è detto, lo slicing consente di ottenere una nuova lista che contiene gli elementi di un intervallo di indici specificato. Tuttavia, è possibile specificare gli indici in modo implicito.

In particolare, se si vuole ottenere una nuova lista che contiene gli elementi di una lista a partire da un certo indice fino all'ultimo elemento, è sufficiente specificare l'indice iniziale e lasciare vuoto l'indice finale.

l = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j']
intervallo = l[5:]

Il risultato sarà una nuova lista che contiene gli elementi con indice 5, 6, 7, 8 e 9. In particolare, la nuova lista sarà:

['f', 'g', 'h', 'i', 'j']

Analogamente, se si vuole ottenere una nuova lista che contiene gli elementi di una lista fino ad un certo indice escluso, è sufficiente specificare l'indice finale e lasciare vuoto l'indice iniziale.

l = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j']
intervallo = l[:5]

Il risultato sarà una nuova lista che contiene gli elementi con indice 0, 1, 2, 3 e 4. In particolare, la nuova lista sarà:

['a', 'b', 'c', 'd', 'e']
Definizione

Slicing con indici impliciti

Nell'applicare lo slicing di una lista, è possibile omettere l'indice iniziale oppure l'indice finale.

Omettendo il primo indice, si specifica che si vogliono ottenere gli elementi della lista a partire dal primo elemento.

nuova_lista = lista[indice_iniziale:]

Omettendo il secondo indice, si specifica che si vogliono ottenere gli elementi della lista fino all'ultimo elemento.

nuova_lista = lista[:indice_finale]

Ovviamente, se si omettono entrambi gli indici, si ottiene una copia della lista originale. Ad esempio:

l = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j']
intervallo = l[:]

Il risultato sarà:

['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j']

Slicing con indici negativi

Nell'applicare lo slicing ad una lista possiamo anche usare gli indici negativi. In particolare, gli indici negativi sono utili per indicare gli elementi della lista a partire dalla fine.

Ad esempio, se vogliamo ottenere gli ultimi 3 elementi di una lista, possiamo fare:

l = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j']
intervallo = l[-3:]

Il risultato sarà una nuova lista che contiene gli elementi con indice 7, 8 e 9. In particolare, la nuova lista sarà:

['h', 'i', 'j']

Analogamente, se vogliamo ottenere gli elementi con indice compreso tra 2 e 5 (elemento con indice 5 escluso), possiamo scrivere:

l = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j']
intervallo = l[-8:-5]

Il risultato sarà il seguente:

['c', 'd', 'e']
Definizione

Slicing con indici negativi

Nell'applicare lo slicing di una lista, è possibile usare gli indici negativi per indicare gli elementi della lista a partire dalla fine.

Condizione sull'indice iniziale e finale

Quando si usa lo slicing bisogna prestare attenzione ai valori dell'indice iniziale e finale. Infatti l'indice iniziale deve essere maggiore dell'indice finale, anche quando usiamo indici negativi. In caso contrario otteniamo una lista vuota.

Ad esempio prendiamo la lista che segue:

l = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

Se eseguiamo la riga seguente:

print(l[3:5])

otteniamo il seguente risultato:

[4, 5]

Questo perché l'indice iniziale è minori dell'indice finale. Se invece eseguiamo la riga seguente:

print(l[5:3])

otteniamo il seguente risultato:

[]

Questo perché l'indice iniziale è maggiore dell'indice finale.

Nota

Condizione sull'indice iniziale e finale dello slicing

Quando si usa lo slicing di una lista, l'indice iniziale deve essere minore dell'indice finale. In caso contrario si ottiene una lista vuota.

Slicing con step

Lo slicing consente di specificare anche uno step o passo. Lo step è un numero intero che indica di quanti elementi si vuole saltare nel momento in cui si estraggono gli elementi della lista.

Per intenderci, se prendiamo una sottolista specificando, tramite slicing, un intervallo che va da indice_iniziale a indice_finale, normalmente la sottolista conterrà gli elementi con indice che va da indice_iniziale a indice_finale - 1.

Ad esempio, se specifichiamo l'intervallo come [3:6], la sottolista avrà gli elementi con indice 3, 4 e 5. L'incremento, o step, tra un indice e il successivo è pari a 1.

Se invece specifichiamo l'intervallo come [3:6:2], dove il terzo elemento rappresenta lo step, la sottolista avrà gli elementi con indice 3 e 5. Questo perché:

  1. Verrà preso il primo elemento con indice 3;
  2. Verrà preso l'elemento con indice 3 + 2 = 5; quindi verrà saltato l'elemento con indice 4;
  3. L'elemento successivo dovrebbe essere quello con indice 5 + 2 = 7. Ma poiché l'indice 7 è maggiore dell'indice finale dello slicing, 6, questo elemento non verrà aggiunto alla sottolista.

Prendiamo un esempio:

l = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
intervallo = l[3:6:2]

Il risultato sarà:

[4, 6]

Ricapitolando:

Definizione

Slicing con step

Nell'applicare lo slicing ad una lista si può specificare anche uno step o passo.

Lo step è un numero intero che indica di quanti elementi va incrementato l'indice, a partire dall'indice iniziale, per ottenere gli elementi della sottolista.

La sintassi per specificare lo step è la seguente:

lista[indice_iniziale:indice_finale:step]

Formalmente la lista risultante conterrà tutti gli elementi con l'indice che rispetta la seguente condizione:

\text{indice iniziale} + \text{step} \cdot i \leq \text{indice finale}

Slicing e Assegnamento

Fino a questo punto abbiamo visto come usare lo slicing per ottenere una nuova lista come sottoinsieme di una lista esistente. Quindi abbiamo usato lo slicing in lettura.

In realtà lo slicing può essere anche combinato con l'assegnamento per modificare una lista esistente. Vediamo come possiamo usarlo in tal senso.

Slicing per la modifica

Supponiamo di avere una lista di stringhe:

l = ["giallo", "verde", "rosso", "blu"]

Se vogliamo modificare gli elementi con indice 2 e 3 potremmo fare:

l[2] = "ciano"
l[3] = "magenta"

Tuttavia abbiamo dovuto usare due assegnamenti. Possiamo sintetizzare questa modifica applicando lo slicing:

l[2:4] = ["ciano", "magenta"]

Il risultato sarà:

["giallo", "verde", "ciano", "magenta"]

In questo modo abbiamo modificato gli elementi con indice 2 e 3 con un solo assegnamento.

Slicing per l'inserimento

Nell'esempio di sopra abbiamo usato lo slicing per modificare in un colpo solo più elementi della lista originale. Possiamo anche usare lo slicing per inserire nuovi elementi in una lista.

Supponiamo di avere la seguente lista:

l = ["lunedì", "martedì", "domenica"]

Se vogliamo inserire la stringa "giovedì" tra "martedì" e "domenica" possiamo fare:

l[2:2] = ["giovedì"]

Il risultato sarà:

["lunedì", "martedì", "giovedì", "domenica"]

Avremmo anche potuto inserire più elementi in un colpo solo:

l[2:2] = ["mercoledì", "giovedì", "venerdì", "sabato"]

Il risultato sarà:

["lunedì", "martedì", "mercoledì", "giovedì", "venerdì", "sabato", "domenica"]

In generale, per l'inserimento con lo slicing, l'indice iniziale e finale devono essere uguali. In questo modo si specifica che gli elementi da inserire devono essere inseriti proprio in quella posizione.

Sfruttando questo meccanismo possiamo anche inserire elementi in testa o in coda alla lista. Per fare questo possiamo usare gli indici speciali 0 e -1:

l[0:0] = ["domenica scorsa"]
l[len(l):len(l)] = ["lunedì prossimo"]

Il risultato sarà:

["domenica scorsa", "lunedì", "martedì", "mercoledì", "giovedì", "venerdì", "sabato", "domenica", "lunedì prossimo"]

Slicing per la cancellazione

Possiamo anche usare lo slicing per cancellare elementi da una lista. Supponiamo di avere la seguente lista:

l = ["lunedì", "martedì", "mercoledì", "giovedì", "venerdì", "sabato", "domenica"]

Se vogliamo cancellare gli elementi con indice 2, 3 e 4 possiamo fare:

l[2:5] = []

Il risultato sarà:

["lunedì", "martedì", "sabato", "domenica"]

In sostanza, quello che abbiamo fatto è stato di aver assegnato alla sottolista con indice 2, 3 e 4 una lista vuota. Abbiamo usato la stessa tecnica dello slicing per la modifica ma assegnando una lista vuota.

In Sintesi

In questa lezione abbiamo visto la tecnica fondamentale dello slicing, dall'inglese affettamento, per ottenere sottoliste da liste.

Nello slicing è possibile specificare:

  • l'indice iniziale;
  • l'indice finale;
  • lo step.

Come indici possiamo usare anche valori negativi, come abbiamo già fatto per l'indicizzazione normale. Tuttavia, dobbiamo sempre garantire che l'indice iniziale sia maggiore di quello finale per evitare di ottenere una sottolista vuota.

Inoltre, lo slicing può essere usato anche per modificare una lista esistente, per inserire nuovi elementi o per cancellare elementi.