14 Istruzioni
In Python, un programma è composto da una sequenza di istruzioni che l’interprete esegue una dopo l’altra. Le istruzioni sono i comandi fondamentali inviati al sistema operativo per la generazione delle attività computazionali da parte dell’hardware.
Ogni istruzione rappresenta un’azione, come la creazione di una variabile, l’esecuzione di una iterazione di ciclo, la definizione di una funzione o la stampa di un messaggio sullo schermo.
Le istruzioni si distinguono in semplici e composte:
Le istruzioni semplici sono quelle che non contengono altre istruzioni al loro interno. Sono eseguite dall’interprete come un singolo blocco di codice.
Le istruzioni composte contengono altre istruzioni al loro interno. Queste istruzioni definiscono blocchi di codice che possono includere altre istruzioni semplici o composte e che non possono essere vuoti.
14.1 Istruzione di gestione identificatori
14.1.1 Assegnamenti
Gli assegnamenti in Python sono istruzioni semplici che collegano valori a variabili utilizzando l’operatore =
. L’assegnamento in Python è una definizione e non può mai far parte di un’espressione. Per eseguire un’assegnamento come parte di un’espressione, è necessario utilizzare l’operatore :=
(noto come operatore “walrus”).
Esempio di assegnamento semplice:
- 1
-
Assegna il valore
10
alla variabilex
. - 2
-
Assegna il valore
20
alla variabiley
. - 3
-
Output della somma di
x
ey
:30
.
Esempio di assegnamento con l’operatore walrus:
- 1
-
Assegna il valore
10
an
e verifica sen
è maggiore di5
. - 2
-
Output di
n
:10
.
14.1.2 Importazione di moduli
L’istruzione semplice import
viene utilizzata per importare moduli in un programma Python, permettendo l’accesso alle funzioni, classi e variabili definite al loro interno.
Tutti gli identificatori definiti nel modulo nome_modulo
si importano colla sintassi import nome_modulo
e dal quel punto fino alla fine del modulo importatore, sono accessibili colla notazione data dal nome del modulo seguito dal punto e l’identificatore di interesse, cioè nome_modulo.nome_variabile
dove nome_variabile
è l’identificatore importato.
Una sintassi alternativa è from nome_modulo import *
, per cui gli identificatori sono utilizzabili senza preporre nome_modulo.
.
Infine, si possono importare identificatori particolari usando from nome_modulo import
seguito dall’elenco degli identificatori necessari, separati da virgole.
Esempi:
Importazione di tutti gli identificatori di un modulo:
- 1
-
Si importa il modulo
math
. - 2
-
Output:
2.0
.
Importazione di identificatori particolari:
- 1
-
Output:
2.0
. - 2
-
Output:
3.141592653589793
.
Importazione di un modulo con un alias:
- 1
-
numpy
è una librarie non facente parte dello standard Python, importata solitamente con un identificatore abbreviato innp
. - 2
-
Output:
[1 2 3]
.
Importazione di tutti gli identificatori di un modulo con accesso semplificato:
- 1
-
Output:
2.0
. - 2
-
Output:
3.141592653589793
.
È importante notare che l’istruzione import
carica e inizializza il modulo solo una volta per sessione del programma. Se il modulo è già stato importato in precedenza, Python utilizza la versione già caricata, riducendo così il tempo di esecuzione e il consumo di memoria.
Quando si importa un modulo, Python cerca il modulo nelle directory specificate nella variabile sys.path
. Questa variabile include la directory corrente, le directory specificate nella variabile d’ambiente PYTHONPATH
, e le directory di installazione predefinite.
Esempio:
import sys
1print(sys.path)
- 1
- Elenco delle directory del computer dove Python cerca i moduli.
14.2 Istruzioni di controllo di flusso
Il flusso di controllo di un programma regola l’ordine in cui le istruzioni vengno eseguite. Il flusso di controllo di un programma Python dipende principalmente da istruzioni condizionali, cicli e chiamate a funzioni.
Anche il sollevamento e la gestione delle eccezioni influenzano il flusso di controllo (tramite le istruzioni try
e with
).
14.2.1 Istruzione di esecuzione condizionale
Spesso è necessario eseguire alcune istruzioni solo quando una certa condizione è vera o scegliere le istruzioni da eseguire a seconda di condizioni mutuamente esclusive. L’istruzione composta if
, che comprende le clausole if
, elif
ed else
, consente di eseguire condizionalmente blocchi di istruzioni.
La sintassi dell’istruzione if
in pseudocodice è la seguente:
1if espressione:
2 istruzione(i)
3elif espressione:
4 istruzione(i)
5elif espressione:
6 istruzione(i)
7else:
8 istruzione(i)
- 1
-
Clausola
if
con una condizione, cioè una espressione con valore interpretato come logico. - 2
-
Blocco di codice eseguito se la condizione
if
è vera. - 3
-
Clausola
elif
con una condizione. - 4
-
Blocco di codice eseguito se la condizione
elif
è vera. - 5
-
Clausola
elif
con una condizione. - 6
-
Blocco di codice eseguito se la condizione
elif
è vera. - 7
-
Clausola
else
eseguita se tutte le condizioni precedenti sono false. - 8
-
Blocco di codice eseguito dalla clausola
else
.
Le clausole elif
ed else
sono opzionali. Ecco un tipico esempio di istruzione if
con tutti e tre i tipi di clausole:
1if x < 0:
2 print('x è negativo')
3elif x % 2:
4 print('x è positivo e dispari')
else:
5 print('x è pari e non negativo')
- 1
-
Controlla se
x
è negativo. - 2
-
Stampa
"x è negativo"
se la condizione è vera. - 3
-
Controlla se
x
è positivo e dispari. - 4
-
Stampa
"x è positivo e dispari"
se la condizione è vera. - 5
-
Stampa
"x è pari e non negativo"
se nessuna delle condizioni precedenti è vera.
Ogni clausola controlla una o più istruzioni raggruppate in un blocco di codice: si posizionano le istruzioni del blocco su righe logiche separate dopo la riga contenente la parola chiave della clausola (nota come riga intestazione della clausola), con un’indentazione tipicamente di quattro spazi oltre la riga intestazione. Il blocco termina quando l’indentazione torna al livello della riga intestazione della clausola o ulteriormente a sinistra (questo è lo stile imposto da PEP 8).
È possibile utilizzare qualsiasi espressione Python come condizione in una clausola if
o elif
. Utilizzare un’espressione in questo modo è noto come usarla in un contesto booleano. In questo contesto, qualsiasi valore viene considerato vero o falso. Qualsiasi numero diverso da zero o contenitore non vuoto (stringa, tupla, lista, dizionario, set, ecc.) viene valutato come vero, mentre zero (0, di qualsiasi tipo numerico), None
e contenitori vuoti vengono valutati come falsi.
Per testare un valore x
in un contesto booleano, utilizzare lo stile di codifica seguente:
Questa è la forma più chiara e più “Pythonica”.
Non utilizzare nessuna delle seguenti forme:
C’è una differenza cruciale tra dire che un’espressione restituisce True
(significa che l’espressione restituisce il valore 1 con il tipo bool
) e dire che un’espressione viene valutata come vera (significa che l’espressione restituisce qualsiasi risultato che è vero in un contesto booleano). Quando si testa un’espressione, ad esempio in una clausola if
, interessa solo come viene valutata, non cosa, precisamente, restituisce. Come menzionato in precedenza, “valutata come vera” è spesso espresso informalmente come “è veritiera”, e “valutata come falsa” come “è falsa”.
Quando la condizione della clausola if
viene valutata come vera, le istruzioni all’interno della clausola if
vengono eseguite, quindi l’intera istruzione if
termina. Altrimenti, Python valuta la condizione di ciascuna clausola elif
, in ordine. Le istruzioni all’interno della prima clausola elif
la cui condizione viene valutata come vera, se presente, vengono eseguite e l’intera istruzione if
termina. Altrimenti, quando esiste una clausola else
, essa viene eseguita. In ogni caso, le istruzioni successive all’intera costruzione if
, allo stesso livello, vengono eseguite successivamente.
14.2.2 Istruzione di pattern matching
L’istruzione match
introduce il pattern matching strutturale nel linguaggio Python. Questa funzionalità consente di testare facilmente la struttura e il contenuto degli oggetti Python.
La struttura sintattica generale dell’istruzione match
è la seguente:
1match espressione:
2 case pattern1 [if guard1]:
3 istruzione(i)
case pattern2 [if guard2]:
4 istruzione(i)
...
5 case _:
6 istruzione(i)
- 1
-
La parola chiave
match
seguita da un’espressione il cui valore diventa il soggetto del matching. - 2
-
Clausole
case
indentate che controllano l’esecuzione del blocco di codice che contengono. Possono includere un’opzioneif guard
per ulteriori controlli. - 3
-
Blocco di istruzioni da eseguire se
pattern1
corrisponde. - 4
-
Blocco di istruzioni da eseguire se
pattern2
corrisponde. - 5
- Pattern wildcard che corrisponde a qualsiasi valore.
- 6
- Blocco di codice eseguito se nessun altro pattern corrisponde.
Durante l’esecuzione, Python prima valuta l’espressione, quindi testa il valore risultante contro il pattern in ciascuna clausola case
a turno, dall’inizio alla fine, fino a quando uno corrisponde. A quel punto, il blocco di codice indentato all’interno della clausola case
corrispondente viene eseguito. Un pattern può fare due cose:
Verificare che il soggetto sia un oggetto con una struttura particolare.
Associare componenti corrispondenti a nomi per un uso successivo (di solito all’interno della clausola
case
associata).
Quando un pattern corrisponde al soggetto, il guard
consente un controllo finale prima della selezione della clausola per l’esecuzione. Tutti i binding dei nomi del pattern sono già avvenuti, e si possono usare nel guard
. Quando non c’è un guard
, o quando il guard
viene valutato come vero, il blocco di codice indentato della clausola case
viene eseguito, dopo di che l’esecuzione dell’istruzione match
è completa e non vengono controllate ulteriori clausole.
L’istruzione match
, di per sé, non prevede un’azione predefinita. Se ne serve una, l’ultima clausola case
deve specificare un pattern wildcard, uno la cui sintassi garantisce che corrisponda a qualsiasi valore del soggetto. È un errore di sintassi seguire una clausola case
con un pattern wildcard con ulteriori clausole case
.
Gli elementi del pattern non possono essere creati in anticipo, associati a variabili e riutilizzati in più punti. La sintassi del pattern è valida solo immediatamente dopo la parola chiave case
, quindi non c’è modo di eseguire tale assegnazione. Per ogni esecuzione di un’istruzione match
, l’interprete è libero di memorizzare nella cache le espressioni del pattern che si ripetono all’interno delle clausole, ma la cache inizia vuota per ogni nuova esecuzione.
Esempio di utilizzo dell’istruzione match
:
1def azione(comando, livello):
2 match comando:
3 case "start" if livello > 1:
4 print("Avvio con livello alto")
5 case "start":
6 print("Avvio con livello basso")
7 case "stop" if livello > 1:
8 print("Arresto con livello alto")
9 case "stop":
10 print("Arresto con livello basso")
11 case "pause":
12 print("Pausa")
13 case _:
14 print("Comando sconosciuto")
15azione("start", 2)
16azione("start", 1)
17azione("pause", 3)
18azione("exit", 0)
- 1
-
Definizione della funzione
azione
con due parametri:comando
elivello
. - 2
-
Inizia il blocco
match
per il valorecomando
. - 3
-
Pattern
"start"
con guard che verifica selivello
è maggiore di 1. - 4
-
Output se
comando
è"start"
elivello
è maggiore di 1. - 5
-
Pattern
"start"
senza guard. - 6
-
Output se
comando
è"start"
elivello
è minore o uguale a 1. - 7
-
Pattern
"stop"
con guard che verifica selivello
è maggiore di 1. - 8
-
Output se
comando
è"stop"
elivello
è maggiore di 1. - 9
-
Pattern
"stop"
senza guard. - 10
-
Output se
comando
è"stop"
elivello
è minore o uguale a 1. - 11
-
Pattern
"pause"
. - 12
-
Output se
comando
è"pause"
. - 13
- Pattern wildcard che corrisponde a qualsiasi altro valore.
- 14
-
Output se
comando
non corrisponde a nessun altro pattern. - 15
-
Chiamata a
azione
con"start"
e livello2
. Output:Avvio con livello alto
. - 16
-
Chiamata a
azione
con"start"
e livello1
. Output:Avvio con livello basso
. - 17
-
Chiamata a
azione
con"pause"
e livello3
. Output:Pausa
. - 18
-
Chiamata a
azione
con"exit"
e livello0
. Output:Comando sconosciuto
.
In questo esempio, la guardia if livello > 1
aggiunge una condizione extra per i casi "start"
e "stop"
, permettendo di distinguere tra diversi livelli di comando.
14.2.2.1 Pattern letterali
I pattern letterali corrispondono a valori letterali come interi, float, stringhe, ecc. La corrispondenza viene effettuata confrontando il valore del soggetto con il valore del pattern.
Esempio:
1def controlla_valore(valore):
2 match valore:
3 case 1:
4 return "Uno"
5 case "ciao":
6 return "Saluto"
7 case True:
8 return "Vero"
9 case None:
10 return "Nessuno"
11 case _:
12 return "Altro"
13print(controlla_valore(1))
14print(controlla_valore("ciao"))
15print(controlla_valore(False))
- 1
-
Definizione della funzione
controlla_valore
. - 2
-
Inizia il blocco
match
per il valorevalore
. - 3
-
Pattern letterale
1
. - 4
-
Output se
valore
è1
. - 5
-
Pattern letterale
"ciao"
. - 6
-
Output se
valore
è"ciao"
. - 7
-
Pattern letterale
True
. - 8
-
Output se
valore
èTrue
. - 9
-
Pattern letterale
None
. - 10
-
Output se
valore
èNone
. - 11
- Pattern wildcard che corrisponde a qualsiasi altro valore.
- 12
-
Output se
valore
non corrisponde a nessun altro pattern. - 13
-
Chiamata a
controlla_valore
con1
. Output:Uno
. - 14
-
Chiamata a
controlla_valore
con"ciao"
. Output:Saluto
. - 15
-
Chiamata a
controlla_valore
conFalse
. Output:Altro
.
14.2.2.2 Pattern di cattura
I pattern di cattura utilizzano nomi non qualificati (nomi senza punti) per catturare valori all’interno di un pattern. Questi nomi sono wildcard che corrispondono a qualsiasi valore, ma con un effetto collaterale: il nome viene associato all’oggetto corrispondente nella corrispondente espressione di pattern matching. I binding creati dai pattern di cattura rimangono disponibili dopo l’esecuzione dell’istruzione match
, permettendo alle istruzioni all’interno del blocco case
e al codice successivo di processare i valori catturati.
Un pattern di cattura semplice associa il nome della variabile al valore corrispondente. Se il pattern di cattura è combinato con altre forme di pattern, può catturare parti specifiche del soggetto.
Esempio di utilizzo dei pattern di cattura:
1def descrivi_valore(valore):
2 match valore:
3 case x if x < 0:
4 print(f"{x} è un numero negativo")
5 case x if x == 0:
6 print(f"{x} è zero")
7 case x if x > 0 and x % 2 == 0:
8 print(f"{x} è un numero positivo pari")
9 case x if x > 0 and x % 2 != 0:
10 print(f"{x} è un numero positivo dispari")
11 case _ if isinstance(valore, str):
12 print(f'"{valore}" è una stringa')
13 case _ if isinstance(valore, list):
14 print(f"{valore} è una lista")
15 case _:
16 print("Tipo di valore non riconosciuto")
# Esempi di utilizzo della funzione
17descrivi_valore(-5)
18descrivi_valore(0)
19descrivi_valore(4)
20descrivi_valore(7)
21descrivi_valore("ciao")
22descrivi_valore([1, 2, 3])
23descrivi_valore({"chiave": "valore"})
- 1
-
Definizione della funzione
descrivi_valore
. - 2
-
Inizio del blocco
match
per il valorevalore
. - 3
-
Pattern di cattura
x
con guardx < 0
. - 4
-
Output se
valore
è un numero negativo. Esempio di output:-5 è un numero negativo
. - 5
-
Pattern di cattura
x
con guardx == 0
. - 6
-
Output se
valore
è zero. Esempio di output:0 è zero
. - 7
-
Pattern di cattura
x
con guardx > 0 and x % 2 == 0
. - 8
-
Output se
valore
è un numero positivo pari. Esempio di output:4 è un numero positivo pari
. - 9
-
Pattern di cattura
x
con guardx > 0 and x % 2 != 0
. - 10
-
Output se
valore
è un numero positivo dispari. Esempio di output:7 è un numero positivo dispari
. - 11
-
Pattern di guard
isinstance(valore, str)
. - 12
-
Output se
valore
è una stringa. Esempio di output:"ciao" è una stringa
. - 13
-
Pattern di guard
isinstance(valore, list)
. - 14
-
Output se
valore
è una lista. Esempio di output:[1, 2, 3] è una lista
. - 15
- Pattern wildcard che corrisponde a qualsiasi altro valore.
- 16
-
Output se
valore
non corrisponde a nessun altro pattern. Esempio di output:Tipo di valore non riconosciuto
. - 17
-
Chiamata a
descrivi_valore
con-5
. Output:-5 è un numero negativo
. - 18
-
Chiamata a
descrivi_valore
con0
. Output:0 è zero
. - 19
-
Chiamata a
descrivi_valore
con4
. Output:4 è un numero positivo pari
. - 20
-
Chiamata a
descrivi_valore
con7
. Output:7 è un numero positivo dispari
. - 21
-
Chiamata a
descrivi_valore
con"ciao"
. Output:"ciao" è una stringa
. - 22
-
Chiamata a
descrivi_valore
con[1, 2, 3]
. Output:[1, 2, 3] è una lista
. - 23
-
Chiamata a
descrivi_valore
con{"chiave": "valore"}
. Output:Tipo di valore non riconosciuto
.
14.2.2.3 Pattern a valore
I pattern a valore utilizzano nomi qualificati per rappresentare valori piuttosto che catturarli. In questo modo, puoi fare riferimento a valori specifici all’interno di un pattern senza rischiare di sovrascrivere variabili esistenti. I nomi qualificati possono essere attributi di una classe o attributi di istanze di classe.
Poiché i nomi semplici catturano i valori durante il pattern matching, è necessario utilizzare riferimenti agli attributi (nomi qualificati come nome.attr
) per esprimere valori che possono cambiare tra le diverse esecuzioni dello stesso statement match
.
Esempio di utilizzo dei pattern a valore:
class Valori:
V1 = 42
V2 = "ciao"
V3 = [1, 2, 3]
obj = Valori()
1obj.V4 = 99
2def controlla_valore(valore):
3 match valore:
4 case Valori.V1:
5 print("Valore uguale a 42")
6 case Valori.V2:
7 print('Valore uguale a "ciao"')
8 case Valori.V3:
9 print("Valore uguale a [1, 2, 3]")
10 case obj.V4:
11 print("Valore uguale a 99")
12 case _:
13 print("Valore non riconosciuto")
# Esempi di utilizzo della funzione
14controlla_valore(42)
15controlla_valore("ciao")
16controlla_valore([1, 2, 3])
17controlla_valore(99)
18controlla_valore(100)
- 1
-
Assegna un nuovo attributo
V4
all’istanzaobj
della classeValori
. - 2
-
Definizione della funzione
controlla_valore
. - 3
-
Inizia il blocco
match
per il valorevalore
. - 4
-
Pattern a valore per
Valori.V1
. - 5
-
Output se
valore
è uguale a42
. Esempio di output:Valore uguale a 42
. - 6
-
Pattern a valore per
Valori.V2
. - 7
-
Output se
valore
è uguale a"ciao"
. Esempio di output:Valore uguale a "ciao"
. - 8
-
Pattern a valore per
Valori.V3
. - 9
-
Output se
valore
è uguale a[1, 2, 3]
. Esempio di output:Valore uguale a [1, 2, 3]
. - 10
-
Pattern a valore per
obj.V4
. - 11
-
Output se
valore
è uguale a99
. Esempio di output:Valore uguale a 99
. - 12
- Pattern wildcard che corrisponde a qualsiasi altro valore.
- 13
-
Output se
valore
non corrisponde a nessun altro pattern. Esempio di output:Valore non riconosciuto
. - 14
-
Chiamata a
controlla_valore
con42
. Output:Valore uguale a 42
. - 15
-
Chiamata a
controlla_valore
con"ciao"
. Output:Valore uguale a "ciao"
. - 16
-
Chiamata a
controlla_valore
con[1, 2, 3]
. Output:Valore uguale a [1, 2, 3]
. - 17
-
Chiamata a
controlla_valore
con99
. Output:Valore uguale a 99
. - 18
-
Chiamata a
controlla_valore
con100
. Output:Valore non riconosciuto
.
In questo esempio, Valori.V1
, Valori.V2
, e Valori.V3
sono attributi della classe Valori
, mentre obj.V4
è un attributo dell’istanza obj
della classe Valori
.
14.2.2.4 Pattern OR
I pattern OR utilizzano l’operatore |
per combinare più pattern. Il match ha successo se uno qualsiasi dei pattern combinati corrisponde al soggetto.
Esempio di utilizzo dei pattern OR:
1def descrivi_numero(numero):
2 match numero:
3 case 0 | 1:
4 print("Numero è 0 o 1")
5 case 2 | 3:
6 print("Numero è 2 o 3")
7 case _ if numero > 3:
8 print("Numero è maggiore di 3")
9 case _:
10 print("Numero non riconosciuto")
# Esempi di utilizzo della funzione
11descrivi_numero(0)
12descrivi_numero(2)
13descrivi_numero(5)
14descrivi_numero(-1)
- 1
-
Definizione della funzione
descrivi_numero
. - 2
-
Inizia il blocco
match
per il valorenumero
. - 3
-
Pattern OR per
0 | 1
. - 4
-
Output se
numero
è0
o1
. Esempio di output:Numero è 0 o 1
. - 5
-
Pattern OR per
2 | 3
. - 6
-
Output se
numero
è2
o3
. Esempio di output:Numero è 2 o 3
. - 7
-
Pattern di guard per
numero > 3
. - 8
-
Output se
numero
è maggiore di3
. Esempio di output:Numero è maggiore di 3
. - 9
- Pattern wildcard che corrisponde a qualsiasi altro valore.
- 10
-
Output se
numero
non corrisponde a nessun altro pattern. Esempio di output:Numero non riconosciuto
. - 11
-
Chiamata a
descrivi_numero
con0
. Output:Numero è 0 o 1
. - 12
-
Chiamata a
descrivi_numero
con2
. Output:Numero è 2 o 3
. - 13
-
Chiamata a
descrivi_numero
con5
. Output:Numero è maggiore di 3
. - 14
-
Chiamata a
descrivi_numero
con-1
. Output:Numero non riconosciuto
.
14.2.2.5 Pattern di gruppo
I pattern di gruppo utilizzano parentesi per raggruppare parti di un pattern, consentendo la combinazione di pattern complessi. Questa funzionalità è utile quando si vuole applicare operazioni di pattern matching su componenti specifici di un soggetto.
Esempio di utilizzo dei pattern di gruppo:
1def analizza_tupla(tupla):
2 match tupla:
3 case (x, (y, z)):
4 print(f"Primo elemento: {x}, Secondo elemento: ({y}, {z})")
5 case (x, y):
6 print(f"Primo elemento: {x}, Secondo elemento: {y}")
7 case _:
8 print("Pattern non riconosciuto")
# Esempi di utilizzo della funzione
9analizza_tupla((1, (2, 3)))
10analizza_tupla((1, 4))
11analizza_tupla((1, 2, 3))
- 1
-
Definizione della funzione
analizza_tupla
. - 2
-
Inizia il blocco
match
per il valoretupla
. - 3
-
Pattern di gruppo
(x, (y, z))
. - 4
-
Output se
tupla
corrisponde al pattern(x, (y, z))
. Esempio di output:Primo elemento: 1, Secondo elemento: (2, 3)
. - 5
-
Pattern di gruppo
(x, y)
. - 6
-
Output se
tupla
corrisponde al pattern(x, y)
. Esempio di output:Primo elemento: 1, Secondo elemento: 4
. - 7
- Pattern wildcard che corrisponde a qualsiasi altro valore.
- 8
-
Output se
tupla
non corrisponde a nessun altro pattern. Esempio di output:Pattern non riconosciuto
. - 9
-
Chiamata a
analizza_tupla
con(1, (2, 3))
. Output:Primo elemento: 1, Secondo elemento: (2, 3)
. - 10
-
Chiamata a
analizza_tupla
con(1, 4)
. Output:Primo elemento: 1, Secondo elemento: 4
. - 11
-
Chiamata a
analizza_tupla
con(1, 2, 3)
. Output:Pattern non riconosciuto
.
In questo esempio, il pattern (x, (y, z))
confronta la tupla tupla
per verificare se il secondo elemento è una tupla di due elementi, mentre il pattern (x, y)
confronta la tupla tupla
per verificare se ha esattamente due elementi. Questo esempio mostra come utilizzare i pattern di gruppo in Python per eseguire il pattern matching su tuple.
14.2.2.6 Pattern di sequenza
I pattern di sequenza corrispondono a sequenze come liste o tuple. Ogni elemento della sequenza viene confrontato con il pattern corrispondente. È possibile utilizzare l’asterisco (*
) per catturare più elementi in una sottosequenza.
Esempio:
1def verifica_sequenza(sequenza):
2 match sequenza:
3 case [1, 2, 3]:
4 return "Sequenza 1, 2, 3"
5 case [x, y, z]:
6 return f"Sequenza generica: {x}, {y}, {z}"
7 case [x, *y, z]:
8 return f"Sequenza con primo e ultimo elemento: {x}, {z}, e mediano: {y}"
9 case _:
10 return "Altro"
11print(verifica_sequenza([1, 2, 3]))
12print(verifica_sequenza([4, 5, 6]))
13print(verifica_sequenza([7, 8, 9, 10]))
14print(verifica_sequenza([7, 8]))
- 1
-
Definizione della funzione
verifica_sequenza
. - 2
-
Inizia il blocco
match
per il valoresequenza
. - 3
-
Pattern di sequenza
[1, 2, 3]
. - 4
-
Output se
sequenza
è[1, 2, 3]
. - 5
-
Pattern di sequenza generico
[x, y, z]
per una sequenza di esattamente tre elementi. - 6
- Output con i valori catturati.
- 7
-
Pattern di sequenza con l’uso dell’asterisco (
*y
) per catturare tutti gli elementi intermedi tra il primo (x
) e l’ultimo (z
). - 8
- Output con il primo, l’ultimo e gli elementi intermedi della sequenza.
- 9
- Pattern wildcard che corrisponde a qualsiasi altro valore.
- 10
-
Output se
sequenza
non corrisponde a nessun altro pattern. - 11
-
Chiamata a
verifica_sequenza
con[1, 2, 3]
. Output:Sequenza 1, 2, 3
. - 12
-
Chiamata a
verifica_sequenza
con[4, 5, 6]
. Output:Sequenza generica: 4, 5, 6
. - 13
-
Chiamata a
verifica_sequenza
con[7, 8, 9, 10]
. Output:Sequenza con primo e ultimo elemento: 7, 10, e mediano: [8, 9]
. - 14
-
Chiamata a
verifica_sequenza
con[7, 8]
. Output:Altro
.
In questo esempio, l’uso dell’asterisco *
nel pattern [x, *y, z]
permette di catturare una sottosequenza di lunghezza arbitraria tra il primo e l’ultimo elemento della sequenza. Questo rende possibile gestire sequenze di lunghezza variabile, mentre il pattern [x, y, z]
corrisponde solo a sequenze di esattamente tre elementi.
14.2.2.7 Pattern as
È possibile utilizzare i pattern as
per catturare i valori abbinati da pattern più complessi o componenti di un pattern, che i semplici pattern di cattura non possono gestire. Quando P1
è un pattern, allora P1 as name
è anche un pattern; quando P1
ha successo, Python associa il valore abbinato al nome name
nel namespace locale.
Esempio di utilizzo del pattern as
:
def analizza_comando(comando):
match comando:
case ("start", param) as c:
1 print(f"Avvio con parametro {param}. Comando completo: {c}")
case ("stop", param) as c:
2 print(f"Arresto con parametro {param}. Comando completo: {c}")
case ("pause", param) as c:
3 print(f"Pausa con parametro {param}. Comando completo: {c}")
case _ as c:
4 print(f"Comando sconosciuto: {c}")
# Esempi di utilizzo
5analizza_comando(("start", 5))
6analizza_comando(("stop", 10))
7analizza_comando(("pause", 15))
8analizza_comando(("exit", 20))
- 1
- Caso in cui il comando è un avvio con un parametro.
- 2
- Caso in cui il comando è un arresto con un parametro.
- 3
- Caso in cui il comando è una pausa con un parametro.
- 4
- Caso wildcard che cattura qualsiasi altro comando.
- 5
-
Chiamata a
analizza_comando
con("start", 5)
. Output:Avvio con parametro 5. Comando completo: ('start', 5)
. - 6
-
Chiamata a
analizza_comando
con("stop", 10)
. Output:Arresto con parametro 10. Comando completo: ('stop', 10)
. - 7
-
Chiamata a
analizza_comando
con("pause", 15)
. Output:Pausa con parametro 15. Comando completo: ('pause', 15)
. - 8
-
Chiamata a
analizza_comando
con("exit", 20)
. Output:Comando sconosciuto: ('exit', 20)
.
14.2.2.8 Pattern di mappatura
I pattern di mappatura corrispondono alle mappature definite nel linguaggio come i dizionari. Ogni coppia chiave-valore viene confrontata con il pattern corrispondente.
Esempio:
1def verifica_mappatura(mappatura):
2 match mappatura:
3 case {"a": 1, "b": 2}:
4 return "Mappatura a=1, b=2"
5 case {"a": x, "b": y}:
6 return f"Mappatura generica: a={x}, b={y}"
7 case _:
8 return "Altro"
9print(verifica_mappatura({"a": 1, "b": 2}))
10print(verifica_mappatura({"a": 3, "b": 4}))
11print(verifica_mappatura({"c": 5}))
- 1
-
Definizione della funzione
verifica_mappatura
. - 2
-
Inizia il blocco
match
per il valoremappatura
. - 3
-
Pattern di mappatura
{"a": 1, "b": 2}
. - 4
-
Output se
mappatura
è{"a": 1, "b": 2}
. - 5
-
Pattern di mappatura generico
{"a": x, "b": y}
. - 6
- Output con i valori catturati.
- 7
- Pattern wildcard che corrisponde a qualsiasi altro valore.
- 8
-
Output se
mappatura
non corrisponde a nessun altro pattern. - 9
-
Chiamata a
verifica_mappatura
con{"a": 1, "b": 2}
. Output:Mappatura a=1, b=2
. - 10
-
Chiamata a
verifica_mappatura
con{"a": 3, "b": 4}
. Output:Mappatura generica: a=3, b=4
. - 11
-
Chiamata a
verifica_mappatura
con{"c": 5}
. Output:Altro
.
14.2.2.9 Pattern di classe
I pattern di classe permettono di verificare se un oggetto è un’istanza di una particolare classe e di accedere ai suoi attributi. Un pattern di classe ha la forma generale nome_o_attr(patterns)
, dove nome_o_attr
è un nome semplice o qualificato legato a una classe, e patterns
è una lista separata da virgole di specifiche di pattern.
Se non ci sono specifiche di pattern, il pattern di classe corrisponde a qualsiasi istanza della classe data. Se ci sono specifiche di pattern posizionali, queste devono precedere qualsiasi specifica di pattern nominata.
Le classi predefinite di Python come bool
, bytearray
, bytes
, dict
, float
, frozenset
, int
, list
, set
, str
e tuple
sono tutte configurate per accettare un singolo pattern posizionale, che viene confrontato con il valore dell’istanza.
Esempio:
class Punto:
def __init__(self, x, y):
self.x = x
self.y = y
def descrivi_punto(p):
match p:
case Punto(0, 0):
1 return "Punto all'origine"
case Punto(x, 0):
2 return f"Punto sull'asse x a {x}"
case Punto(0, y):
3 return f"Punto sull'asse y a {y}"
case Punto(x, y) if x == y:
4 return f"Punto sulla bisettrice x=y a ({x}, {y})"
case Punto(x, y):
5 return f"Punto a ({x}, {y})"
case _:
6 return "Non è un punto"
# Esempi di utilizzo
p1 = Punto(0, 0)
p2 = Punto(3, 0)
p3 = Punto(0, 4)
p4 = Punto(2, 2)
p5 = Punto(1, 5)
7print(descrivi_punto(p1))
8print(descrivi_punto(p2))
9print(descrivi_punto(p3))
10print(descrivi_punto(p4))
11print(descrivi_punto(p5))
- 1
-
Pattern di classe che verifica se
p
è un’istanza diPunto
conx
ey
uguali a0
. - 2
-
Pattern di classe che verifica se
p
è un’istanza diPunto
cony
uguale a0
. - 3
-
Pattern di classe che verifica se
p
è un’istanza diPunto
conx
uguale a0
. - 4
-
Pattern di classe con guard che verifica se
p
è un’istanza diPunto
conx
uguale ay
. - 5
-
Pattern di classe generico che verifica se
p
è un’istanza diPunto
e cattura i valori dix
ey
. - 6
- Pattern wildcard che corrisponde a qualsiasi altro valore.
- 7
-
Chiamata a
descrivi_punto
conPunto(0, 0)
. Output:Punto all'origine
. - 8
-
Chiamata a
descrivi_punto
conPunto(3, 0)
. Output:Punto sull'asse x a 3
. - 9
-
Chiamata a
descrivi_punto
conPunto(0, 4)
. Output:Punto sull'asse y a 4
. - 10
-
Chiamata a
descrivi_punto
conPunto(2, 2)
. Output:Punto sulla bisettrice x=y a (2, 2)
. - 11
-
Chiamata a
descrivi_punto
conPunto(1, 5)
. Output:Punto a (1, 5)
.
In questo esempio, Punto
è una classe con attributi x
e y
. I pattern di classe permettono di verificare se un oggetto è un’istanza di Punto
e di accedere ai suoi attributi per ulteriori controlli.
14.3 Cicli
14.3.1 Istruzione while
L’istruzione composta while
ripete l’esecuzione di un blocco di istruzioni fintantoché un’espressione condizionale risulta vera. La clausola else
viene eseguita quando il ciclo while
termina naturalmente (cioè, la condizione del ciclo diventa falsa).
Ecco la sintassi completa dello pseudocodice per l’istruzione while
con la clausola else
:
- 1
- L’interprete valuta l’espressione condizionale.
- 2
-
Se l’espressione condizionale è vera, esegue il blocco di istruzioni all’interno del ciclo
while
. Al termine del blocco, torna a valutare l’espressione condizionale. - 3
-
Se l’espressione condizionale è falsa, esegue il blocco di istruzioni all’interno della clausola
else
. - 4
-
Il blocco di istruzioni all’interno della clausola
else
viene eseguito solo se il ciclowhile
termina naturalmente (cioè, la condizione del ciclo diventa falsa).
Esempio pratico con while
e else
:
- 1
-
Inizia il ciclo
while
finchéx
è maggiore di 0. - 2
-
Stampa il valore corrente di
x
. - 3
-
La clausola
else
viene eseguita quando il ciclowhile
termina naturalmente. - 4
- Stampa “Ciclo terminato”.
L’istruzione while
può anche includere una clausola else
e le istruzioni break
e continue
. L’istruzione break
interrompe il ciclo while
, al pari di return
se il ciclo si trova in una funzione. L’istruzione continue
salta l’iterazione corrente e passa alla successiva.
Esempio:
- 1
-
Inizia il ciclo
while
finchéx
è maggiore di0
. - 2
-
Interrompe il ciclo quando
x
è uguale a5
. - 3
-
Salta l’iterazione corrente se
x
è pari. - 4
-
Stampa il valore di
x
se non è stato saltato.
14.3.2 Istruzione for
L’istruzione for
ripete l’esecuzione di un blocco di istruzioni controllata da un’espressione iterabile. La sintassi è:
- 1
-
indice
è normalmente un identificatore che funge da variabile di controllo del ciclo e viene associato a ciascun elemento dell’iterabile
. - 2
-
istruzione(i)
rappresenta una o più istruzioni che vengono eseguite per ogni elemento dell’iterabile.
Esempio tipico di for
:
- 1
-
Inizia il ciclo
for
su ogni carattere della stringa'ciao'
. - 2
-
Stampa
"Dammi una [lettera]..."
per ogni lettera della stringa.
L’istruzione for
può anche includere una clausola else
che viene eseguita se il ciclo termina normalmente (cioè, non viene interrotto da un’istruzione break
o return
).
numeri = [1, 2, 3, 4, 5]
1for numero in numeri:
2 if numero == 3:
break
3 print(numero)
4else:
5 print("Ciclo completato")
- 1
-
Inizia il ciclo
for
sulla listanumeri
. - 2
-
Se il numero è 3, interrompe il ciclo con
break
. - 3
- Stampa il numero corrente.
- 4
-
La clausola
else
viene eseguita se il ciclofor
termina naturalmente. - 5
- Stampa “Ciclo completato”.
L’iterabile può essere qualsiasi espressione iterabile in Python. In particolare, qualsiasi sequenza è iterabile. L’interprete chiama implicitamente la funzione built-in iter()
sull’iterabile, producendo un iteratore.
14.3.3 Iteratori e iterabili
Un iteratore è un oggetto che permette di attraversare una collezione di elementi, uno alla volta. Gli iteratori sono utilizzati per rappresentare flussi di dati o collezioni di elementi che non sono necessariamente tutti disponibili in memoria contemporaneamente. Pertanto:
Un oggetto è iterabile se può restituire un iteratore. Un oggetto iterabile implementa il metodo
__iter__()
che deve restituire un iteratore.Un oggetto è un iteratore se implementa i metodi
__iter__()
e__next__()
. Il metodo__next__()
restituisce il prossimo elemento della collezione e solleva l’eccezioneStopIteration
quando non ci sono più elementi.
Esempio di iteratore:
1numeri = [1, 2, 3]
2iteratore = iter(numeri)
3print(next(iteratore))
4print(next(iteratore))
5print(next(iteratore))
6print(next(iteratore))
- 1
- Definizione di una lista di numeri.
- 2
- Creazione di un iteratore dall’iterabile numeri.
- 3
-
Output:
1
. - 4
-
Output:
2
. - 5
-
Output:
3
. - 6
-
Solleva l’eccezione
StopIteration
perché non ci sono più elementi.
L’istruzione for
chiama implicitamente iter
sul suo iterabile per ottenere un iteratore. Lo pseudocodice seguente mostra cosa accade dietro le quinte:
14.3.4 La funzione range
La funzione range
genera una sequenza di numeri interi. È comunemente usata nei cicli for
.
- 1
-
Inizia il ciclo
for
sui numeri da0
a4
. - 2
- Stampa il numero corrente.
range
può accettare fino a tre argomenti: start
, stop
, e step
.
- 1
-
Inizia il ciclo
for
sui numeri da1
a9
, con incremento di2
. - 2
- Stampa il numero corrente.
L’oggetto range
è un iterabile ma non un iteratore. Tuttavia, è possibile ottenere un iteratore chiamando iter()
su un oggetto range
.
- 1
-
Stampa il primo numero dell’oggetto
range
:0
. - 2
-
Stampa il secondo numero dell’oggetto
range
:1
.
14.3.5 Spacchettamento nei cicli for
È possibile utilizzare un indice composto da più identificatori, come in un’assegnazione con spacchettamento. In questo caso, gli elementi dell’iteratore devono essere essi stessi iterabili, ciascuno con esattamente tanti elementi quanti sono gli identificatori nell’indice.
Esempio di ciclo for
su un dizionario:
d = {'a': 1, 'b': 2, 'c': 3}
1for chiave, valore in d.items():
2 if chiave and valore:
3 print(chiave, valore)
- 1
-
Inizia il ciclo
for
sugli elementi del dizionariod
. - 2
- Verifica se sia la chiave che il valore sono “truthy”.
- 3
- Stampa la coppia chiave-valore.
È possibile usare un identificatore preceduto da un asterisco *
nel target. Questo identificatore verrà associato a una lista di tutti gli elementi non assegnati ad altri target.
lista = [1, 2, 3, 4, 5]
1for primo, *centro, ultimo in [lista]:
2 print(primo)
3 print(centro)
4 print(ultimo)
- 1
-
Inizia il ciclo
for
sulla listalista
. - 2
- Stampa il primo elemento della lista.
- 3
- Stampa tutti gli elementi centrali della lista.
- 4
- Stampa l’ultimo elemento della lista.
14.4 Comprensioni
Le comprensioni comprehension in Python sono un modo conciso e leggibile per creare nuove sequenze (liste, insiemi, dizionari) da iterabili esistenti. Sono un esempio di supporto alla programmazione funzionale in quanto permettono di creare nuove collezioni attraverso la trasformazione e il filtraggio di elementi, senza modificare l’iterabile originale. Questa capacità di trasformare dati in modo dichiarativo, senza effetti collaterali, è un principio fondamentale della programmazione funzionale.
14.4.1 Liste
Le comprensioni di liste sono uno degli utilizzi più comuni. Permettono di ispezionare ogni elemento di un iterabile e costruire una nuova lista aggiungendo i risultati di un’espressione calcolata su alcuni o tutti gli elementi. Una compresa di lista ha la seguente sintassi:
indice
eiterabile
in ogni clausolafor
di una compresa di lista hanno la stessa sintassi e significato di quelli in una normale istruzionefor
.espressione
può essere qualsiasi espressione Python valida e viene calcolata per ogni elemento della lista risultante.clausole
è una serie di zero o più clausole, ciascuna con la formafor indice in iterabile
oif espressione
.
Esempio semplice:
- 1
- Crea una lista di numeri incrementati di 1 da 0 a 4.
- 2
-
Output:
[1, 2, 3, 4, 5]
.
Esempio con condizione:
- 1
- Crea una lista con i numeri da 1 a 5, ma solo per i numeri maggiori di 2.
- 2
-
Output:
[4, 5]
.
Esempio con annidamento:
lista_di_liste = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
1result = [x for sublist in lista_di_liste for x in sublist]
2print(result)
- 1
- Appiattisce una lista di liste in una singola lista.
- 2
-
Output:
[1, 2, 3, 4, 5, 6, 7, 8, 9]
.
14.4.2 Insiemi
Le comprensioni di insiemi hanno la stessa sintassi e semantica delle comprese di liste, ad eccezione che sono racchiuse tra parentesi graffe {}
invece che tra parentesi quadre []
. Il risultato è un insieme.
Esempio:
- 1
-
Crea un insieme con i risultati della divisione intera di
n
per 2, da 0 a 9. - 2
-
Output:
[0, 1, 2, 3, 4]
.
14.4.3 Dizionari
Le comprensioni di dizionari hanno la stessa sintassi delle comprese di insiemi, ma invece di una singola espressione prima della clausola for
, si usano due espressioni separate da un due punti :
: chiave:valore
. Il risultato è un dizionario che mantiene l’ordine di inserimento.
Esempio:
- 1
- Crea un dizionario associando ogni stringa al suo indice nella lista.
- 2
-
Output:
{'zero': 0, 'uno': 1, 'due': 2}
.
14.5 Gestione anomalie
14.5.1 Istruzioni try
e raise
Python supporta la gestione delle eccezioni con l’istruzione composta try
, che include le clausole try
, except
, finally
ed else
. Il codice può anche sollevare esplicitamente un’eccezione con l’istruzione raise
. Quando il codice solleva un’eccezione, il normale flusso di controllo del programma si interrompe e Python cerca un gestore di eccezioni adatto.
Esempio di utilizzo di try
e raise
:
1def dividi(a, b):
2 try:
3 return a / b
4 except ZeroDivisionError:
5 raise ValueError("Divisione per zero non consentita")
6try:
7 risultato = dividi(10, 0)
8except ValueError as e:
9 print(e)
- 1
-
Definizione della funzione
dividi
. - 2
-
Inizia il blocco
try
. - 3
- Tentativo di divisione.
- 4
-
Gestione dell’eccezione
ZeroDivisionError
. - 5
-
Sollevamento di una nuova eccezione
ValueError
. - 6
-
Inizia un altro blocco
try
. - 7
-
Tentativo di chiamare
dividi
con un denominatore pari a zero. - 8
-
Gestione dell’eccezione
ValueError
. - 9
-
Output del messaggio di errore:
Divisione per zero non consentita
.
14.5.2 Istruzione di controllo condizioni anomale
L’istruzione assert condizione, messaggio
viene utilizzata per eseguire controlli durante l’esecuzione del programma. Se la condizione
è falsa, viene sollevata un’eccezione AssertionError
che include il messaggio
. Può essere usata per il debugging e per verificare condizioni che dovrebbero essere sempre vere in un punto specifico del codice.
Esempi di utilizzo:
- 1
-
Non genera errore perché
x > 0
è vero. - 2
-
Genera
AssertionError
con il messaggio"x deve essere minore di zero"
perchéx < 0
è falso.
È importante notare che le asserzioni possono essere disabilitate a livello di runtime utilizzando l’opzione -O
(ottimizzazione) quando si esegue lo script Python. Questo rimuoverà tutte le istruzioni assert
dal bytecode generato.
14.6 Altre istruzioni
14.6.1 Istruzione pass
Il corpo di un’istruzione composta in Python non può essere vuoto; deve contenere almeno un’istruzione. Si può utilizzare l’istruzione pass
, che non esegue alcuna azione, come segnaposto esplicito quando è richiesta un’istruzione sintatticamente ma non c’è nulla da fare.
Esempio di utilizzo di pass
:
- 1
- Condizione sempre vera.
- 2
- Segnaposto che non esegue alcuna azione.
- 1
- Definizione di una funzione.
- 2
- Segnaposto per una funzione non ancora implementata.
- 1
- Definizione di una classe.
- 2
- Segnaposto per una classe non ancora implementata.
14.6.2 Istruzione with
L’istruzione composta with
può spesso essere un’alternativa più leggibile e utile all’istruzione try/finally
. Essa consente di gestire risorse in modo efficiente e sicuro, assicurando che le risorse siano correttamente rilasciate dopo l’uso. Per essere gestita dall’istruzione with
, una risorsa deve implementare il protocollo del context manager, che richiede i metodi speciali __enter__
e __exit__
.
Un esempio comune è l’uso di with
per gestire i file:
- 1
-
Apre il file
file.txt
in modalità lettura. - 2
- Legge il contenuto del file.
- 3
- Output del contenuto del file.
Esempio di un contesto personalizzato:
class GestoreRisorsa:
1 def __enter__(self):
2 print("Risorsa acquisita")
return self
3 def __exit__(self, tipo, valore, traceback):
4 print("Risorsa rilasciata")
5with GestoreRisorsa() as risorsa:
6 print("Usando la risorsa")
- 1
-
Metodo
__enter__
che viene chiamato all’inizio del bloccowith
. - 2
- Output indicante che la risorsa è stata acquisita.
- 3
-
Metodo
__exit__
che viene chiamato alla fine del bloccowith
, indipendentemente dal fatto che ci sia stata un’eccezione o meno. - 4
- Output indicante che la risorsa è stata rilasciata.
- 5
-
Inizia il blocco
with
usando ilGestoreRisorsa
. - 6
- Output indicante l’uso della risorsa.
14.6.3 Istruzioni di ritorno
Le istruzioni di ritorno vengono utilizzate per restituire valori da una funzione. Esistono due tipi principali di istruzioni di ritorno: return
e yield
.
14.6.3.1 return
L’istruzione return
viene utilizzata per restituire un valore da una funzione e terminare l’esecuzione della funzione.
- 1
-
Definizione della funzione
somma
. - 2
-
Restituisce la somma di
a
eb
. - 3
-
Output della somma:
7
.
14.6.3.2 yield
L’istruzione yield
viene utilizzata per restituire un valore da una funzione generatore senza terminare l’esecuzione della funzione. La funzione può riprendere l’esecuzione dal punto in cui è stata interrotta al successivo ciclo di iterazione.
- 1
-
Definizione della funzione generatore
generatore
. - 2
-
Restituisce
1
e sospende l’esecuzione. - 3
-
Restituisce
2
e sospende l’esecuzione. - 4
-
Restituisce
3
e sospende l’esecuzione. - 5
- Itera sui valori restituiti dal generatore.
- 6
-
Output dei valori:
1
,2
,3
.
14.6.4 Modificatori di ambito
Le istruzioni global
e nonlocal
sono utilizzate per modificare la visibilità delle variabili all’interno delle funzioni.
L’istruzione global
viene utilizzata per dichiarare che una variabile all’interno di una funzione fa riferimento a una variabile globale, cioè una variabile definita al di fuori di qualsiasi funzione. Senza global
, tutte le assegnazioni di variabili all’interno di una funzione sono considerate locali alla funzione stessa.
Esempio di utilizzo di global
:
- 1
-
x
è una variabile globale. - 2
-
L’istruzione
global
dichiara chex
fa riferimento alla variabile globalex
, permettendo alla funzione di modificarla. - 3
-
Output:
20
.
L’istruzione nonlocal
viene utilizzata per dichiarare che una variabile all’interno di una funzione fa riferimento a una variabile non locale, cioè una variabile definita in un contesto esterno ma non globale (ad esempio, all’interno di una funzione contenente). Senza nonlocal
, tutte le assegnazioni di variabili all’interno di una funzione sono considerate locali alla funzione stessa.
Esempio di utilizzo di nonlocal
:
- 1
-
x
è una variabile locale alla funzioneesterna
. - 2
-
L’istruzione
nonlocal
dichiara chex
fa riferimento alla variabile non localex
, permettendo alla funzioneinterna
di modificarla. - 3
-
Output:
20
.
14.6.5 Alias di tipo
L’istruzione type
viene utilizzata per creare alias di tipo. Questo consente di assegnare nomi significativi ai tipi di dati complessi, migliorando la leggibilità del codice. Si possono definire anche alias di tipi generici, cioè tipi parametrizzati da altri tipi.
È importante sottolineare che gli alias non sono da intendere come utili al controllo statico dei tipi durante l’esecuzione, ma come annotazione utile per strumenti di analisi del codice e miglioramento della leggibilità.
Esempi:
Definizione di alias di tipo:
1type lista_coppie = list[tuple[str, int]]
- 1
-
lista_coppie
è un alias dilist[tuple[str, int]]
.
Definizione di alias di tipo generico:
1type contenitore_ordinato[T] = list[t] | tuple[T, ...]
- 1
-
contenitore_ordinato
può essere o una lista o una tupla con zero o più elementi.
14.6.6 Eliminazione di identificatori e elementi in contenitori
L’istruzione del
viene utilizzata per eliminare identificatori come variabili o di funzione e oggetti da contenitori, come elementi singoli o in sezioni (slice) di liste oppure chiavi di dizionari. del
rimuovendo i riferimenti agli oggetti, segnala al garbage collector la possibilità di liberazione delle risorse associate.
Esempi di utilizzo:
Eliminazione di una variabile:
- 1
-
Elimina la variabile
x
. - 2
-
Dà errore perché
x
non esiste.
Eliminazione di un elemento da un contenitore di oggetti di tipo lista per indice:
- 1
- Elimina il primo elemento dalla lista.
- 2
-
Output:
[2, 3, 4]
.
Eliminare di elementi contigui da una lista:
- 1
- Elimina gli elementi dal secondo al quarto.
- 2
-
Output:
[1, 5, 6]
.
Eliminazione di una chiave da un dizionario:
- 1
-
Elimina la chiave
'a'
dal dizionario. - 2
-
Output:
{'b': 2, 'c': 3}
.
Eliminazione di un attributo da un oggetto:
class ClasseSemplice: def __init__(self): self.attr = 42 oggetto_semplice = ClasseSemplice() 1del oggetto_semplice.attr
- 1
-
Elimina l’attributo
'attr'
dall’oggetto'oggetto_semplice'
.
Eliminazione dell’identificatore di una funzione:
- 1
-
Elimina il riferimento
somma_semplice
alla funzione. - 2
-
Errore. Output:
NameError: name 'somma_semplice' is not defined
.
Eliminazione dell’identificatore di un modulo:
- 1
-
Output:
2.0
. - 2
-
Errore. Output:
NameError: name 'math' is not defined. Did you forget to import 'math'?
.