13 Il modello dati
Per comprendere il modello dati di Python, dobbiamo conoscere i tipi di dati del linguaggio e le relative operazioni, con ciò intendendo che dobbiamo sia elencare quelli predefiniti, che apprendere le modalità di definizione di nuovi tipi.
13.1 Elementi di programmazione orientata agli oggetti
Gli oggetti sono l’astrazione dei dati definita in Python. Ogni oggetto è caratterizzato da un’identità, un tipo e un valore. L’identità di un oggetto non cambia una volta creato e possiamo pensarlo come l’indirizzo dell’oggetto in memoria1. Python permette di ricavarlo per mezzo della funzione predefinita (built-in) id()
:
1 In CPython è effettivamente implementato così.
s = "Hello"
1print(id(s))
- 1
-
Output: un numero come
4467381744
.
Il tipo di un oggetto determina le operazioni che l’oggetto supporta per la manipolazione del proprio stato o di quello di altri oggetti, e definisce anche i possibili valori per gli oggetti di quel tipo (dominio dei valori, per prendere in prestito un termine dalla matematica). Il linguaggio fornisce una funzione per conoscere il tipo dell’oggetto:
s = "Hello"
1print(type(s))
- 1
-
Output:
<class 'str'>
.
13.1.1 Tipi e classi
In Python, i tipi di dati sono implementati come classi. Una classe è una definizione che specifica una struttura di dati e il comportamento associato attraverso attributi e metodi. Gli attributi sono variabili legate alla classe o alle sue istanze e rappresentano lo stato, mentre i metodi sono funzioni che definiscono il comportamento.
Un’istanza (o oggetto) è un’implementazione concreta della classe. Mentre di una classe esiste una sola definizione, si possono creare infinite istanze della stessa classe, a meno che non siano state implementate limitazioni particolari. Ogni istanza ha il proprio stato (valore degli attributi). Gli attributi e i metodi possono essere membri sia delle classi che delle istanze. In generale, ogni oggetto può avere i propri attributi e metodi, ma può anche accedere agli attributi e ai metodi della classe a cui appartiene.
Gli attributi possono essere di classe o di istanza:
Gli attributi di classe sono condivisi tra tutte le istanze della classe.
Gli attributi di istanza sono specifici di ogni istanza.
I metodi di istanza operano su istanze specifiche della classe e possono accedere e modificare gli attributi dell’istanza stessa. I metodi di classe, invece, operano a livello della classe e possono accedere e modificare gli attributi della classe attraverso il parametro cls
. Infine, i metodi statici sono funzioni associate alla classe che non dipendono né dalla classe né dalle sue istanze; non possono modificare lo stato della classe o delle sue istanze.
Ecco un esempio di definizione di una classe in Python, utile, per il momento, solo a mostrare la sintassi dei membri:
class Esempio:
1 attributo_di_classe = 'Valore di classe'
def __init__(self, attributo):
2 self.attributo_di_istanza = attributo
3 def metodo_di_istanza(self):
return f"Metodo di istanza: {self.attributo_di_istanza}"
@classmethod
4 def metodo_di_classe(cls):
return f"Metodo di classe: {cls.attributo_di_classe}"
@staticmethod
5 def metodo_statico():
return "Metodo statico"
6oggetto = Esempio("Valore di istanza")
7print(oggetto.metodo_di_istanza())
8print(Esempio.metodo_di_classe())
9print(Esempio.metodo_statico())
- 1
- Attributo di classe.
- 2
- Attributo di istanza.
- 3
- Metodo di istanza.
- 4
-
Metodo di classe, preceduto da
@classmethod
. - 5
-
Metodo statico, preceduto da
@staticmethod
. - 6
-
Creazione di un oggetto della classe
Esempio
associato alla variabileoggetto
. - 7
-
Output:
Metodo di istanza: Valore di istanza
. - 8
-
Output:
Metodo di classe: Valore di classe
. - 9
-
Output:
Metodo statico
.
In questo esempio, Esempio
è una classe con un attributo di classe, un attributo di istanza, un metodo di istanza, un metodo di classe e un metodo statico. La classe definisce la struttura e il comportamento, mentre l’oggetto oggetto
è un’istanza concreta della classe.
13.1.2 Creazione di oggetti
Una volta definita una classe, si possono creare nuove istanze o oggetti di quella classe, il che rappresenta un modo efficiente per riutilizzare funzionalità nel proprio codice. Questo passaggio è spesso chiamato costruzione o istanziazione degli oggetti. Lo strumento responsabile per la specializzazione di questo processo, è comunemente noto come costruttore della classe.
In Python, il costruttore è il metodo speciale __init__
e può avere un numero arbitrario di parametri. Questo metodo viene eseguito automaticamente dall’interprete quando un nuovo oggetto della classe viene creato ed è il luogo possiamo scrivere delle istruzioni utili all’inizializzazione dell’oggetto, anche usando gli argomenti passati. La sintassi per l’istanziazione è data dall’identificatore della classe, seguita da una coppia di parentesi tonde, con all’interno l’elenco degli argomenti.
Esempio:
1s = str(4)
- 1
-
L’identificatore della classe delle stringhe
str
è seguito da un unico argomento,4
, dato da un letterale intero, passato al costruttore per l’inizializzazione.
Il metodo __init__
è sempre presente: se non è definito esplicitamente nella classe, viene ereditato dalla classe base object
.
La creazione di un oggetto avviene in diversi contesti:
Uso di letterali. Ad esempio, per creare un oggetto stringa, si usano i letterali stringa e analogamente per gli altri:
- 1
-
Creazione di un oggetto di tipo
str
. - 2
-
Creazione di un oggetto di tipo
int
. - 3
-
Creazione di un oggetto di tipo
float
.
Chiamata del costruttore della classe, ad esempio a quello della classe
list
passiamo una sequenza di interi:Utilizzo di funzioni e metodi che restituiscono oggetti. Ad esempio, il metodo
upper()
della classestr
restituisce un nuovo oggetto di tipostr
, ottenuto a partire da quello su cui è eseguito:- 1
-
upper()
crea un secondo oggetto stringa che viene associato alla variabilesu_1
. - 2
- Sintassi alternativa che ha il medesimo effetto.
Clonazione o copia utilizzando la funzione
copy
del modulocopy
:- 1
-
lista_originale
identifica un oggetto lista. - 2
-
copy()
produce una copia dilista_originale
identificata dalista_copiata
.
13.1.3 Accesso a attributi e metodi
In Python, si utilizza la notazione con il punto .
per accedere agli attributi e ai metodi di un oggetto o di una classe, semplicemente accodando all’identificatore della variabile (che rappresenta un’istanza della classe) o a quello della classe, il punto e l’identificatore del membro (attributo o metodo).
Esempio su str
:
- 1
- Creazione di un oggetto stringa.
- 2
-
Chiamata del metodo
upper()
usando l’identificatores
. - 3
-
Output:
HELLO WORLD
. - 4
-
Output dell’attributo
__class__
:<class 'str'>
.
13.1.4 Gerarchie di classi
Le classi sono organizzate in una gerarchia. In cima alla gerarchia delle classi di Python c’è la classe object
, con un nome po’ infelice, da cui derivano tutte le altre classi. Questo significa che ogni classe in Python eredita gli attributi e i metodi della classe object
.
La funzione isinstance()
è utile per investigare questa gerarchia, poiché permette di verificare se un oggetto è un’istanza di una determinata classe o di una sua classe derivata. isinstance()
, pertanto, ha due parametri, l’identificatore dell’oggetto e l’identificatore della classe e restituisce un valore logico, True
o False
.
Ad esempio:
- 1
-
Output:
True
.isinstance
conferma ches
è un’istanza della classestr
. - 2
-
Output:
True
.isinstance
conferma ches
è un’istanza anche della classeobject
, da cui derivastr
.
La funzione issubclass()
è altrettanto utile per esplorare le gerarchie delle classi, in quanto permette di verificare se una classe è una sottoclasse di un’altra. Ha due parametri, cioè il nome della classe derivata e quella base e restituisce un valore logico.
Ad esempio:
1print(issubclass(str, object))
- 1
-
Output:
True
.issubclass
conferma chestr
è una sottoclasse diobject
.
13.1.5 Mutabilità e immutabilità
Gli oggetti il cui valore può cambiare sono detti mutabili, mentre gli oggetti il cui valore non lo può, una volta creati, sono chiamati immutabili. L’immutabilità di un oggetto è determinata dalla progettazione del suo tipo. Ad esempio, per i tipi definiti nel linguaggio, numeri interi e in virgola mobile, stringhe e tuple sono immutabili, mentre dizionari e liste sono mutabili.
Alcuni oggetti, chiamati contenitori, contengono riferimenti ad altri oggetti. Esempi di contenitori sono tuple, liste e dizionari, strutture dati di base definite in molti linguaggi di programmazione. I riferimenti ad altri oggetti contribuiscono al valore di un contenitore e, nella maggior parte dei casi, quando parliamo del valore di un contenitore, intendiamo proprio i valori degli oggetti contenuti. Ad esempio:
- 1
-
Tupla con tre oggetti al suo interno:
1
,2
,3
. Diciamo che la tupla contiene i tre valori interi. - 2
-
Lista con tre oggetti all’interno:
"Qui"
,"Quo"
,"Qua"
. Diciamo che la lista contiene le tre stringhe.
La mutabilità di un contenitore si riferisce alla identità degli oggetti referenziati e non al loro valore, quindi, ad esempio, se una tupla contiene un riferimento ad un oggetto mutabile, il valore della tupla cambia se quell’oggetto mutabile viene modificato:
s = ([1, 2, 3], ["Qui", "Quo", "Qua"])
1print(s)
2print(id(s[0]))
3print(id(s[1]))
4s[1][0] = "Huey"
s[1][1] = "Dewey"
s[1][2] = "Louie"
5print(s)
6print(id(s[0]))
7print(id(s[1]))
- 1
-
Output:
([1, 2, 3], ['Qui', 'Quo', 'Qua'])
. - 2
- Modifico gli elementi della lista, traducendoli in inglese.
- 3
-
Output dell’identità del primo oggetto contenuto (su ogni computer e sessione sarà diverso):
4361472384
. - 4
-
Output dell’identità del secondo oggetto contenuto:
4361474176
. - 5
-
Output:
([1, 2, 3], ['Huey', 'Dewey', 'Louie'])
. La tupla è cambiata! - 6
-
Output:
4361472384
, l’identità del primo oggetto contenuto non è cambiata! - 7
-
Output:
4361474176
, l’identità del secondo oggetto contenuto non è cambiata!
Ciò è un po’ più generale, perché per gli oggetti di tipo immutabile, quando sono assegnati ad una variabile o risultato di un’espressione, a parità di valore, potrebbero avere la stessa identità, cioè essere lo stesso oggetto. Il condizionale è dovuto alla presenza e applicazione logiche di ottimizzazione, ad esempio della memoria, implementate nell’interprete.
Ad esempio, dopo:
- 1
-
Output dell’identità di
s1
:4432491760
. - 2
-
Output dell’identità di
s2
:4432491760
, cioè è la medesima identità dell’oggetto il cui nome ès1
.
Per gli oggetti mutabili, questo non accade anche in casi su cui potrebbe essere ottimizzante:
- 1
-
Output dell’identità di
l1
:4454287744
. - 2
-
Output dell’identità di
l2
:4454289536
, cioè le identità sono diverse.
Attenzione però ad alcune scorciatoie di Python:
è equivalente a:
cioè l’oggetto lista ha due nomi nel programma.
13.1.6 Tipi hashable
I tipi di dati possono essere classificati come hashable o non hashable. Un tipo è considerato hashable se gli oggetti creati da quel tipo hanno un valore di hash2 che rimane costante per tutta la durata della loro vita e possono essere confrontati con altri oggetti. Questo è garantito per i tipi immutabili, come numeri, stringhe e tuple.
2 In informatica, le tabelle hash (hash table) sono una struttura dati essenziale per l’efficienza e la velocità di accesso ai dati. Le tabelle hash sono costituite da coppie indice-valore e sono progettate per sostituire la ricerca sequenziale in array, che può essere lenta soprattutto per grandi quantità di dati.
Una funzione di hash prende un valore (come una stringa o un numero) e restituisce un numero di lunghezza fissa, chiamato hash. Questo hash viene utilizzato come indice per memorizzare il valore nella tabella di hash. Quando si desidera cercare un elemento, la funzione di hash calcola l’indice corrispondente, consentendo un accesso diretto all’elemento. Questo processo rende la ricerca, l’inserimento e la cancellazione degli elementi molto efficienti, con un tempo di accesso medio costante (O(1)).
Gli oggetti di tipo hashable possiedono le seguenti caratteristiche principali:
Valore di hash costante: Gli oggetti devono avere un valore di hash che non cambia durante la loro vita. Questo è tipico degli oggetti immutabili.
Comparabilità: Gli oggetti devono poter essere confrontati con altri oggetti per determinare se sono uguali. Due oggetti che sono considerati uguali devono avere lo stesso valore di hash.
La proprietà di essere hashable è strettamente legata all’immutabilità. Gli oggetti immutabili sono naturalmente hashable perché il loro stato non cambia dopo la creazione, garantendo che il valore di hash rimanga costante. Ad esempio, tipi di dati immutabili come numeri, stringhe e tuple possono essere considerati hashable. Il viceversa non è vero, cioè esistono alcuni tipi mutabili che sono hashable.
13.1.7 Eliminazione
Gli oggetti non vengono mai eliminati esplicitamente dall’utente, ma, quando diventano inaccessibili, possono essere raccolti dal garbage collector, che è eseguito contemporaneamente al codice del programma, come parte dell’interprete. L’implementazione specifica quando e come la memoria debba essere liberata.
Alcuni oggetti contengono riferimenti a risorse esterne rispetto al programma, come file aperti, connessioni di rete, finestre (graphical user interface, GUI) o dispositivi hardware. Queste risorse generalmente non vengono liberate dal garbage collector, perché le azioni corrispondenti sono particolari della risorsa stessa. Pertanto, i loro oggetti forniscono anche un metodo esplicito di rilascio, solitamente chiamato close()
. È molto importante tener conto di ciò per evitare effetti indesiderati.
13.2 Tipi predefiniti
Python mette a disposizione molti tipi generali, cioè utili alla gestione di dati comunemente presenti in algoritmi. Esistono, inoltre, sia meccanismi di estensione della distribuzione Python che modificano l’installazione sul proprio computer, sia la possibilità conferita al programmatore di creare i propri tipi o di importarli da librerie prodotte da terze parti.
I tipi predefiniti sono immediatamente disponibili, cioè non necessitano di alcuna azione da parte del programmatore, come, ad esempio, l’importazione di moduli.
Per ragioni di esposizione, distingueremo tra tipi predefiniti fondamentali, cioè che possono essere introdotti con un insieme limitato di concetti, tipi legati alla astrazione del paradigma di orientamento agli oggetti e, infine, costruzioni del linguaggio legate all’esecuzione asincrona.
13.2.1 Tipi predefiniti fondamentali
Prima di entrare nel dettaglio per ognuno, di seguito un elenco dei tipi predefiniti fondamentali:
Numeri: Sia interi relativi che reali, cioè in virgola mobile, sia complessi.
Sequenze: Contenitori caratterizzati da un ordinamento degli oggetti al loro interno, sia mutabili che immutabili.
Insiemi: Contenitori non caratterizzati da un ordinamento degli oggetti.
Dizionari: Contenitori di coppie di oggetti, rispettivamente, chiave e valore.
Altri:
None
,NotImplemented
,Ellipsis
.
13.2.2 Numeri
I tipi predefiniti numerici corrispondono agli interi, i numeri a virgola mobile e quelli complessi. Sono immutabili, il che significa che effettuando una operazione su un oggetto numerico si produrrà sempre un nuovo oggetto e non una modifica del precedente.
Esistono anche altri tipi numerici nelle librerie fornite coll’interprete, per i numeri decimali a precisione arbitraria e i numeri razionali come frazioni.
I letterali numerici non hanno segno, cioè +
e -
sono operatori unari che precedono la rappresentazione di un numero, quindi -3
è una espressione che diventa il valore di un oggetto intero di valore -3
. Nei letterali numerici si può inserire un trattino basso (underscore) tra le cifre o dopo gli specificatori della base per gli interi.
13.2.2.1 Interi
Esistono due tipi di interi. Il primo è int
, per gli usuali numeri interi, a cui corrispondono diverse rappresentazioni sintattiche in basi diverse, come i letterali decimali, binari, ottali o esadecimali:
Un letterale decimale inizia sempre con una cifra diversa da zero, esempi:
10
,1_0
.Quello binario inizia con
0b
o0B
seguito da una sequenza di cifre binarie (0 o 1), esempi:0b1010
,0B1010
,0b_10_10
,0b1_0_10
.L’ottale inizia con
0o
o0O
seguito da una sequenza di cifre ottali (da 0 a 7), esempi:0o12
,0O12
.L’esadecimale inizia con
0x
o0X
seguito da una sequenza di cifre esadecimali (da 0 a 9 e da A a F, in maiuscolo o minuscolo), esempi:0xA
,0XA
.
Gli interi in Python sono illimitati, al netto della finitezza della memoria del computer disponibile per la loro rappresentazione.
large_int = 10**100
1print(f"Un intero molto grande: {large_int}")
- 1
-
Output:
10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
.
L’altro tipo è bool
, che ha solo due oggetti possibili, che si comportano come quelli corrispondenti agli interi 1
e 0
per rappresentare i valori logici, rispettivamente, di vero e falso. bool
come classe deriva da int
che, a sua volta, deriva da object
:
- 1
-
Output:
True
. - 2
-
Output:
True
.
13.2.2.2 Numeri in virgola mobile
I numeri in virgola mobile in Python sono istanze della classe float
e sono utilizzati per rappresentare numeri reali con una parte decimale. I letterali corrispondenti sono una sequenza di cifre in base 10, e solo in base 10, che include un punto (.
), un suffisso di notazione scientifica (e
o E
, opzionalmente seguito da +
o -
e da una o più cifre), o entrambi. Il carattere iniziale di un letterale in virgola mobile non può essere e
o E
, ma può essere qualsiasi cifra (anche zeri a differenza degli interi) o un punto (.
) seguito da una cifra.
Esempi di literali in virgola mobile: 0.
, 0.1
, .0
, 1.
, 1.0
, 1e0
, 1.e0
, 1.0E0
, 1_0_0.0_1
. Attenzione: 1
è un intero, 1.
non lo è.
Per conoscere l’intervallo esatto e la precisione dei valori in virgola mobile sulla piattaforma in uso, e molti altri dettagli, è possibile usare sys.float_info
nel modo seguente:
import sys
1float_info = sys.float_info
2print(f"Massimo valore rappresentabile (max): {float_info.max}")
3print(f"Minimo valore rappresentabile positivo (min): {float_info.min}")
4print(f"Massimo esponente base 2 (max_exp): {float_info.max_exp}")
5print(f"Minimo esponente base 2 (min_exp): {float_info.min_exp}")
6print(f"Massimo esponente base 10 (max_10_exp): {float_info.max_10_exp}")
7print(f"Minimo esponente base 10 (min_10_exp): {float_info.min_10_exp}")
8print(f"Precisione in bit (dig): {float_info.dig}")
9print(f"Numero di bit di mantissa (mant_dig): {float_info.mant_dig}")
10print(f"Epsilon macchina (epsilon): {float_info.epsilon}")
- 1
- Oggetto con all’interno le informazioni relative alla gestione dei numeri in virgola mobile della macchina.
- 2
-
Output:
Massimo valore rappresentabile (max): 1.7976931348623157e+308
. Il massimo valore rappresentabile per un numero in virgola mobile. - 3
-
Output:
Minimo valore rappresentabile positivo (min): 2.2250738585072014e-308
. Il minimo valore positivo rappresentabile per un numero in virgola mobile. - 4
-
Output:
Massimo esponente base 2 (max_exp): 1024
. Il massimo esponente base 2. - 5
-
Output:
Minimo esponente base 2 (min_exp): -1021
. Il minimo esponente base 2. - 6
-
Output:
Massimo esponente base 10 (max_10_exp): 308
. Il massimo esponente base 10. - 7
-
Output:
Minimo esponente base 10 (min_10_exp): -307
. Il minimo esponente base 10. - 8
-
Output:
Precisione in bit (dig): 15
. La precisione in bit, cioè il numero di cifre decimali che possono essere rappresentate senza perdita di precisione. - 9
-
Output:
Numero di bit di mantissa (mant_dig): 53
. Il numero di bit nella mantissa. - 10
-
Output:
Epsilon macchina (epsilon): 2.220446049250313e-16
. La differenza tra 1 e il numero più piccolo maggiore di 1 che può essere rappresentato.
13.2.2.3 Numeri complessi
Un numero complesso è composto da due valori in virgola mobile, uno per la parte reale e uno per la parte immaginaria. In Python, i numeri complessi sono istanze della classe complex
, che presenta due attributi di sola lettura real
e imag
, rispettivamente per la parte reale e immaginaria, di tipo float
.
Un letterale immaginario può essere specificato come qualsiasi letterale decimale in virgola mobile o intero seguito da una j
o J
: 0j
, 0.j
, 0.0j
, .0j
, 1j
, 1.j
, 1.0j
, 1e0j
, 1.e0j
, 1.0e0j
. La j
alla fine del letterale immaginario indica la radice quadrata di -1
. Per denotare un qualsiasi numero complesso costante, si potrà sommare o sottrarre un letterale in virgola mobile (o letterale intero) e uno immaginario.
1z = 42+3.14j
2print(z)
3print(z.real)
4print(z.imag)
5print(type(z.real))
6print(type(z.imag))
7print(type(z))
- 1
-
Assegnamento di un numero complesso alla variabile
z
. - 2
-
Output:
(42+3.14j)
. - 3
-
Output:
42.0
. - 4
-
Output:
3.14
. - 5
-
Output:
<class 'float'>
. - 6
-
Output:
<class 'float'>
. - 7
-
Output:
<class 'complex'>
.
13.2.3 Sequenze
Una sequenza è un contenitore ordinato di oggetti, il cui indice è un intero che parte da 0
. Se la sequenza è referenziata da una variabile c
, per ottenere il numero totale di oggetti contenuti si userà la funzione predefinita len()
, cioè len(c)
, e per l’oggetto i-esimo le parentesi quadre e l’indice, c[i]
. È possibile usare indici negativi che sono interpretati come la somma di tale indice col numero totale di oggetti contenuti, quindi -i
è trattato come len(c)-i
e l’indice risultante dovrà sempre essere compreso tra 0
e len(c)-1
.
Si può sezionare (slicing) la sequenza per ottenere una nuova sottosequenza, dello stesso tipo, di oggetti originariamente contigui tra gli indici i
e j
, cioè con indici x
tali che i≤x<j
, usando c[i:j]
. Aggiungendo un terzo parametro di passo, k
, si possono selezionare solo gli oggetti con indici i+l*k
e l≥0
, che siano compresi tra i due indici, cioè i≤i+l*k<j
, con c[i:j:k]
.
Le sequenze sono categorizzate in base alla mutabilità:
Immutabili: stringhe, tuple, bytes.
Mutabili: liste e array di bytes.
13.2.3.1 Iterabilità
Le sequenze sono iterabili, cioè possono essere ottenuti tutti gli oggetti contenuti nell’ordine corretto, per mezzo di un oggetto ad hoc detto iteratore. Un iteratore è un oggetto separato dalla sequenza stessa, che mantiene uno stato interno per tenere traccia dell’elemento successivo da restituire. Questo permette di iterare sulla sequenza senza modificarla direttamente. L’iteratore prende in input un oggetto iterabile e ne restituisce i valori uno alla volta secondo un certo protocollo. Per le sequenze, questo protocollo consiste nel partire dal primo elemento e procedere fino all’ultimo.
13.2.3.2 Stringhe
Una stringa in Python è un oggetto che si può creare a partire da un letterale composto di un numero non negativo di caratteri Unicode racchiusi o tra apici singoli '
, oppure doppi "
. Per inserire un a capo nella stringa dovrà essere usato \n
, mentre per spezzarla su due righe fisiche dovrà essere usato un singolo backslash alla fine della prima riga fisica per indicare la continuazione nella riga fisica successiva.
Esempi:
- 1
- Stringa su due righe fisiche ma senza a capo all’interno.
- 2
-
Output:
Hello World!
. - 3
- Stringa su due righe fisiche con un a capo all’interno.
- 4
-
Output:
Hello
World!
.
Alternativamente, si possono usare letterali con coppie di tripli apici singoli '''
o doppi """
, dove la differenza è che è possibile inserire un a capo nell’editor e sarà mantenuto nella stringa. Un singolo backslash non può essere presente.
- 1
- Stringa su due righe fisiche ma senza a capo all’interno.
- 2
-
Output:
Hello
World!
. - 3
- Stringa su due righe fisiche con un a capo all’interno.
- 4
-
Output:
Hello
World!
.
Nei letterali stringa si possono inserire caratteri non stampabili o caratteri che non sono disponibili a tastiera, usando le cosiddette sequenze di escape, come da tabella seguente:
Sequenza | Significato | Codice ASCII/ISO | Esempio di stringa Python |
---|---|---|---|
\<newline> |
Ignora fine linea | - | "Questo è un testo\ con una linea continuata" |
\\ |
Backslash | 0x5c |
"C:\\percorso\\al\\file" |
\' |
Apice singolo | 0x27 |
"L'apice singolo: \' esempio" |
\" |
Apice doppio | 0x22 |
"L'apice doppio: \" esempio" |
\a |
Campanello | 0x07 |
"Suono del campanello\a" |
\b |
Backspace | 0x08 |
"Carattere di backspace\b" |
\f |
Form feed | 0x0c |
"Form feed\f esempio" |
\n |
Nuova linea | 0x0a |
"Nuova linea\n esempio" |
\r |
Ritorno carrello | 0x0d |
"Ritorno carrello\r esempio" |
\t |
Tabulazione | 0x09 |
"Tabulazione\tesempio" |
\v |
Tabulazione verticale | 0x0b |
"Tabulazione verticale\v esempio" |
\DDD |
Valore ottale DDD del codice Unicode del carattere (solo per caratteri ASCII) | DDD (in ottale) |
"Valore ottale: \101 esempio" (\101 rappresenta 'A' che in ASCII è 65 ) |
\xXX |
Valore esadecimale XX del codice Unicode del carattere | XX (in esadecimale) |
"Valore esadecimale: \x41 esempio" (\x41 rappresenta ‘A’) |
\uXXXX |
Carattere Unicode con valore esadecimale a 4 cifre | XXXX (in esadecimale) |
"Carattere cinese: \u4e2d" (\u4e2d rappresenta ‘中’) |
\UXXXXXXXX |
Carattere Unicode con valore esadecimale a 8 cifre | XXXXXXXX (in esadecinale) |
"Carattere: \U0001f600" (\U0001f600 rappresenta ‘😀’) |
\N{name} |
Carattere Unicode | - | "Carattere Unicode: \N{LATIN CAPITAL LETTER A} esempio" |
Esistono anche i letterali di stringhe grezze (raw), sintatticamente identiche alle altre a meno di un suffisso r
o R
, comportante che le sequenze di escape non siano interpretate. Si usano comunemente per esprimere pattern di espressioni regolari o percorsi di file in Windows.
1stringa = "C:\\Users\\username\\Documents\\file.txt"
2print(stringa)
3stringa_raw = r"C:\Users\username\Documents\file.txt"
4print(stringa_raw)
- 1
- Perché un path sia corretto in Windows è necessario usare il backslash per escape del backslash come separatore di path.
- 2
-
Output:
C:\Users\username\Documents\file.txt
. - 3
- Definendo la stringa come grezza allora il backslash è intepretato come tale, quindi separatore di path.
- 4
-
Output:
C:\Users\username\Documents\file.txt
.
Dopo aver identificato il letterale stringa, l’inteprete crea l’oggetto stringa in memoria con tipo str
. Alternativamente, si può crearla in altri modi:
Sebbene non sia comune usare il costruttore per creare una stringa da un letterale stringa, è comunque possibile:
- 1
-
Passo
"Hello, World!"
al costruttore distr
. - 2
-
Output:
Hello, World!
.
Possiamo creare una stringa a partire da un numero (intero o in virgola mobile):
- 1
-
Output:
3.14
. - 2
-
Output:
42
.
Si può creare una stringa concatenando i caratteri di una lista:
cl = ['H', 'e', 'l', 'l', 'o'] s = ''.join(cl) 1print(s)
- 1
-
Output:
Hello
.
Se un oggetto definisce il metodo speciale
__str__()
che restituisce una stringa, il costruttore distr
lo chiama per ottenerla:l = ["Hello", " ", "World!"] s = str(l) 1print(s)
- 1
-
Output:
['Hello', ' ', 'World!']
.
Si può creare una nuova stringa da qualsiasi oggetto iterabile, come liste o tuple, utilizzando il metodo
join
che inserisce tra gli elementi della sequenza la stringa dell’oggetto di cui è membro:lista = ['Python', 'è', 'fantastico'] s = ' '.join(lista) 1print(s) tupla = ('Hello', 'World!') s = ' '.join(tupla) 2print(s)
- 1
-
Output:
Python è fantastico
. Lo spazio tra le parole è stato inserito perchéjoin
è stato chiamato su un oggetto la cui stringa era data dal solo carattere di spazio. - 2
-
Output:
Hello World!
.
13.2.3.3 Oggetti bytes
Un oggetto bytes
è un array immutabile i cui elementi sono byte a 8 bit, rappresentati da interi nel range da 0 a 255. Gli oggetti bytes
sono utili per gestire dati binari, come quelli letti o scritti su file o trasmessi su reti.
Esistono diversi modi per creare oggetti bytes
:
È possibile creare oggetti
bytes
utilizzando letterali bytes, che sono simili ai letterali stringa, ma sono preceduti dal prefissob
. Ad esempio:1b'abc'
- 1
-
Crea un oggetto bytes con i byte corrispondenti ai caratteri ASCII
'a'
,'b'
,'c'
.
È possibile creare oggetti
bytes
utilizzando il costruttorebytes()
, che può accettare diversi tipi di argomenti:È possibile creare una stringa a partire da un oggetto
bytes
specificando la codifica:- 1
-
Oggetto di tipo
bytes
. - 2
-
Creazione di una stringa da oggetto
bytes
che è codificato in ASCII. - 3
-
Output:
Hello World!
.
Alcuni esempi di utilizzo degli oggetti bytes
:
Creazione e accesso:
- 1
-
Output:
104
(ASCII per ‘h’). - 2
-
Output:
b'el'
.
Concatenazione e ripetizione:
- 1
-
Output:
b'helloworld'
. - 2
-
Output:
b'hellohellohello'
.
Conversione da e verso stringhe:
- 1
-
Converte la stringa in
bytes
usando la codifica UTF-8. - 2
-
Output:
b'hello'
. - 3
-
Decodifica i
bytes
in una stringa. - 4
-
Output:
'hello'
.
13.2.3.4 Tuple
Le tuple in Python sono sequenze ordinate immutabili, in cui gli oggetti contenuti possono essere di tipi diversi. La classe è tuple
che deriva da object
.
Per creare una tupla, si può utilizzare una serie di espressioni separate da virgole (,
), come elementi della tupla. Si può opzionalmente mettere una virgola ridondante dopo l’ultimo elemento, che è necessaria se si ha un solo elemento. Si possono raggruppare gli elementi della tupla tra parentesi, ma le parentesi sono necessarie solo quando le virgole avrebbero altrimenti un altro significato (ad esempio, nelle chiamate di funzione), o per denotare tuple vuote o annidate.
Esempi di tuple costruite con letterali e espressioni:
- 1
- Tupla con tre oggetti contenuti da letterale numerico, letterale stringa e ancora letterale numerico.
- 2
- Tupla con un solo oggetto da letterale numerico.
- 3
- Tupla con un solo oggetto da espressione.
- 4
- Tupla vuota.
Possiamo creare le tuple anche usando il costruttore di tuple
, che accetta come argomento un iterabile:
- 1
- Crea una tupla vuota.
- 2
- Crea una tupla con 5 stringhe, una per ogni carattere.
- 3
- Crea una tupla con una lista di 3 interi.
Esempi di operazioni sulle tuple:
Accesso e numero di elementi:
t1 = 42., "Hello", 0x42 1print(t1[1]) 2print(t1[1:3]) 3print(42. in t1) 4print(len(t1)) 5print(t1[2:6]) 6print(t1[4:6])
- 1
-
Output:
"Hello"
. - 2
-
Output:
3
. - 3
-
Output:
('Hello', 66)
. Slicing di tupla che produce una nuova tupla con 2 oggetti. - 4
-
Test di appartenenza di
42.
nella tupla(42.0, 'Hello', 66, 42)
. Output:True
. - 5
-
Slicing con indice destro oltre la lunghezza della lista. Output:
(66,)
. - 6
-
Slicing con entrambi gli indici oltre la lunghezza della lista. Output:
()
.
Concatenazione:
t1 = 42., "Hello", 0x42 t2 = (42,) t3 = t1 + t2 1print(t3)
- 1
-
Concatenazione di tuple che produce una nuova tupla con 4 oggetti contenuti. Output:
(42.0, 'Hello', 66, 42)
.
13.2.4 Liste
Le liste in Python sono sequenze ordinate mutabili, in cui gli oggetti contenuti possono essere di tipi diversi. La classe è list
che deriva da object
.
Per creare una lista, si utilizza una serie di espressioni, separate da virgole (,
), all’interno di parentesi quadre, per indicare gli elementi della lista. Si può opzionalmente mettere una virgola ridondante dopo l’ultimo elemento. Per denotare una lista vuota, si utilizza una coppia di parentesi quadre vuote.
Esempi di liste costruite con letterali e espressioni:
- 1
- Lista con tre oggetti: un letterale numerico, un letterale stringa e un altro letterale numerico.
- 2
- Lista con un solo oggetto creato da un’espressione.
- 3
- Lista vuota.
Possiamo creare le liste usando il costruttore della classe list
, passando come argomento un iterabile o nulla:
- 1
- Crea una lista vuota.
- 2
- Crea una lista con 5 stringhe, una per ogni carattere della stringa “Hello”.
Le operazioni comuni sulle liste:
Accesso e modifica:
- 1
-
Output:
"Hello"
. Accesso all’elemento in posizione 1 della lista. - 2
-
Output:
3
. Numero totale di elementi nella listal1
. - 3
-
Modifica della lista con l’assegnazione di un nuovo valore a un indice. Output:
[42.0, 'Ciao', 66]
.
Concatenazione:
- 1
-
Concatenazione di liste che produce una nuova lista. Output:
[42.0, 'Hello', 66, 42]
. - 2
-
Concatenazione sul posto modificando
l2
. Output:[42, 42.0, 'Hello', 66]
.
Estensione e aggiunta di elementi:
- 1
-
Estende
l4
aggiungendo gli elementi dil1
. Output:[42.0, 'Hello', 66]
. - 2
-
Aggiunge
l1
come un singolo elemento alla fine dil5
. Output:[[42.0, 'Hello', 66]]
.
Slicing, rimozione e riassegnazione di elementi:
l1 = [42., "Hello", 0x2A, "Hello", 0o52, "Hello", 42] l2 = [42] 1print(l1[1:3]) l1.remove("Hello") 2print(l1) l1.pop(0) 3print(l1) del l1[0:2] 4print(l1) l1[1:3] = ["Ciao", 24] 5print(l1) l1[1:3] = [] 6print(l1)
- 1
-
Slicing di lista che produce una nuova lista. Output:
['Hello', 66]
. - 2
-
Rimozione della prima occorrenza di elemento dalla lista usando
remove()
che, scorrendo la sequenza, applica un test di uguaglianza per stabilire che l’oggetto nella lista sia uguale a quello passato come argomento e, quindi, lo elimina.remove()
non restituisce l’elemento eliminato. Output:[42.0, 42, 'Hello', 42, 'Hello', 42]
. - 3
-
Rimozione di un elemento della lista usando
pop()
per eliminare l’elemento in posizione indicata dall’indice passato come argomento, qui0
.pop()
restituisce l’elemento eliminato. Output:[42, 'Hello', 42, 'Hello', 42]
. - 4
-
Rimozione di elementi con slicing usando la parola chiave
del
. Output:[42, 'Hello', 42]
. - 5
-
Riassegnazione usando lo slicing. Output:
[42, 'Ciao', 24]
. - 6
-
Eliminazione per mezzo di assegnazione e slicing. Output:
[42]
.
13.2.5 Insiemi
Python ha due tipi predefiniti di insiemi per rappresentare collezioni di elementi unici con ordine arbitrario: set
e frozenset
.
Gli elementi in un set
possono essere di tipi diversi, ma devono essere tutti hashable. Le istanze del tipo set
sono mutabili e quindi non hashable, mentre le istanze del tipo frozenset
sono immutabili e hashable.
Non è possibile avere un set
i cui elementi siano altri set
, ma è possibile avere un set
, o un frozenset
, i cui elementi siano frozenset
.
Entrambi i tipi set
e frozenset
derivano direttamente dalla classe base object
.
13.2.5.1 set
Per denotare un set
, si utilizza una serie di espressioni separate da virgole all’interno di parentesi graffe. Si può opzionalmente mettere una virgola ridondante dopo l’ultimo elemento.
Esempi di letterali di set
:
- 1
-
set
definito per mezzo di letterali e delimitatori. - 2
-
Non esiste un letterale per un
set
vuoto, pertanto si deve usare il costruttoreset()
.
Si può creare una istanza di set
chiamando il costruttore senza argomenti, per un set
vuoto, o con un oggetto iterabile (per un set
i cui elementi sono quelli dell’iterabile).
I set
sono mutabili, il che significa che una volta creati, possono essere modificati. Supportano operazioni come aggiunta, rimozione e controllo dell’esistenza di elementi.
Aggiunta di elementi:
s = {1, 2, 3} s.add(4) 1print(s)
- 1
-
add()
aggiunge un elemento. Output:{1, 2, 3, 4}
.
Rimozione di elementi:
- 1
-
Quando crei un
set
con elementi duplicati, come{1, 2, 3, 4, 3}
, Python rimuove automaticamente i duplicati. Quindi, ilset
s
diventerà{1, 2, 3, 4}
. - 2
-
remove()
elimina l’elemento specificato dalset
. Output:{1, 2, 4}
.
Controllo dell’esistenza di un elemento:
- 1
-
Output:
True
. - 2
-
Output:
False
.
Operazioni insiemistiche:
- 1
-
Operazione di unione insiemistica. Output:
set({1, 2, 3, 4, 5})
. - 2
-
Operazione di intersezione insiemistica. Output:
set({3})
. - 3
-
Operazione di differenza insiemistica. Output:
set({1, 2})
.
13.2.5.2 frozenset
Allo stesso modo, si può creare un frozenset
per mezzo del costruttore, senza argomenti o con un iterabile.
- 1
-
Creazione di un
frozenset
da una lista. Output:frozenset({1, 2, 3, 4})
. - 2
-
Creazione di un
frozenset
vuoto. Output:frozenset()
.
I frozenset
sono immutabili, il che significa che una volta creati, non possono essere modificati. Supportano operazioni di lettura come controllo dell’esistenza di elementi e operazioni insiemistiche (unione, intersezione, differenza), ma non supportano operazioni di modifica. Il comportamento è identico alle operazioni di set
.
13.2.6 Mappature
Le mappature rappresentano insiemi finiti di oggetti indicizzati da insiemi di indici arbitrari. La notazione con le parentesi quadre, a[k]
, seleziona l’elemento indicizzato da k
nella mappatura a
e può essere utilizzata all’interno di espressioni oppure a sinistra di assegnazioni o istruzioni del
. La funzione len()
restituisce il numero di elementi in una mappatura.
Attualmente, esiste un solo tipo di mappatura predefinita in Python: il dizionario. È mutabile e la classe corrispondente è dict
che deriva da object
.
I dizionari rappresentano insiemi finiti di oggetti indicizzati da valori quasi arbitrari, detti chiavi. Le chiavi nei dizionari devono essere di tipi arbitrati ma hashable e, come oggetti, uniche. La ragione è che l’implementazione efficiente dei dizionari richiede che il valore hash di una chiave rimanga costante.
I dizionari preservano l’ordine di inserimento, il che significa che le chiavi verranno prodotte nello stesso ordine in cui sono state aggiunte sequenzialmente al dizionario. Sostituire una chiave esistente non cambia l’ordine; tuttavia, rimuovere una chiave e reinserirla la aggiungerà alla fine invece di mantenerne la posizione precedente.
I dizionari si possono creare per mezzo di una serie di coppie di espressioni, separate da virgole, all’interno di parentesi graffe. Le chiavi e i valori sono separati da due punti. È possibile inserire una virgola dopo l’ultimo elemento.
Esempi:
1d1 = {"a": 42, "b": 2, 42: 3, "b": 24}
2print(d1)
x = 2.
d2 = {2**2: "uno", 2 * x: "due", "hello".upper(): x, }
3print(d2)
4d3 = {}
- 1
- Dizionario con tre coppie chiave-valore.
- 2
-
Le chiavi devono essere uniche quindi l’interprete mantiene solo una chiave con un valore arbitrario. Output:
{'a': 42, 'b': 24, 42: 3}
. - 3
-
Dizionario con due coppie chiave-valore e virgola opzionale in fondo. Output:
{4: 'due', 'HELLO': 2.0}
. - 4
- Dizionario vuoto.
Possiamo creare i dizionari anche usando la classe dict
oltre che i letterali:
- 1
- Crea un dizionario vuoto.
- 2
- Crea un dizionario con tre coppie chiave-valore specificate come argomenti.
- 3
-
Crea un dizionario a partire da un iterabile di tuple. Output:
{'a': 1, 'b': 2, 'c': 3}
.
Operazioni sui dizionari:
Accesso agli elementi:
d = {"a": 1, "b": 2, "c": 3} 1print(d["a"]) 2print(d.get("b")) 3print(d.keys()) 4print(d.values()) 5print(d.items())
- 1
-
Accesso al valore associato alla chiave
"a"
. Output:1
. - 2
-
Utilizzo del metodo
get()
per accedere al valore associato alla chiave"b"
. Output:2
. - 3
-
Accesso a tutte le chiavi del dizionario. Output:
dict_keys(['a', 'b', 'c'])
. - 4
-
Accesso a tutti i valori del dizionario. Output:
dict_values([1, 2, 3])
. - 5
-
Accesso a tutte le coppie chiave-valore del dizionario. Output:
dict_items([('a', 1), ('b', 2), ('c', 3)])
.
Modifica degli elementi:
- 1
-
Modifica del valore associato alla chiave
"b"
. Output:{"a": 1, "b": 20, "c": 3}
. - 2
-
Aggiunta di una nuova coppia chiave-valore. Output:
{"a": 1, "b": 20, "c": 3, "d": 4}
. - 3
-
Output del dizionario dopo le modifiche:
{"a": 1, "b": 20, "c": 3, "d": 4}
.
Rimozione degli elementi:
d = {"a": 1, "b": 2, "c": 3} 1del d["b"] 2print(d) 3valore = d.pop("c") 4print(d) 5print(valore) 6d.clear() 7print(d)
- 1
-
Rimozione della coppia chiave-valore con chiave
"b"
usandodel
. - 2
-
Output dopo la rimozione con
del
. Output:{"a": 1, "c": 3}
. - 3
-
Rimozione della coppia chiave-valore con chiave
"c"
usandopop()
, che restituisce il valore associato. - 4
-
Output dopo la rimozione con
pop()
. Output:{"a": 1}
. - 5
-
Valore rimosso con
pop()
. Output:3
. - 6
-
Rimozione di tutte le coppie chiave-valore usando
clear()
. - 7
-
Output dopo l’uso di
clear()
. Output:{}
.
Operazioni di controllo:
- 1
-
Verifica se la chiave
"a"
è presente nel dizionario. Output:True
. - 2
-
Verifica se la chiave
"z"
è presente nel dizionario. Output:False
.
Lo spacchettamento (unpacking) è una funzionalità che permette di combinare i contenuti di più dizionari in un unico dizionario. Si utilizza l’operatore **
per esplodere i contenuti del singolo dizionario. Alternativamente, si può effettuare l’unione di dizionari per mezzo dell’operatore |
.
Esempi:
d1 = {'a': 1, 'b': 2}
d2 = {'c': 3, 'd': 4}
1d3 = {**d1, **d2}
2print(d3)
3d4 = d1 | d2
4print(d4)
5d5 = dict.fromkeys('a', 1)
6print(d5)
7d6 = dict.fromkeys(['a', 'b', 'c'])
8print(d6)
- 1
-
Spacchettamento dei dizionari
d1
ed2
in un nuovo dizionariod3
. - 2
-
Stampa del dizionario
d3
. Output:{'a': 1, 'b': 2, 'c': 3, 'd': 4}
. - 3
-
Creazione di un nuovo dizionario
d4
unendod1
ed2
per mezzo dell’operatore|
. - 4
-
Stampa del dizionario
d4
. Output:{'a': 1, 'x': 5, 'c': 2}
. - 5
-
Creazione di un dizionario
d5
usandodict.fromkeys
con le chiavi dalla stringa'a'
e valore1
. - 6
-
Stampa del dizionario
d5
. Output:{'a': 1}
. - 7
-
Creazione di un dizionario
d6
usandodict.fromkeys
con le chiavi dalla lista[1, 2, 3]
e valoriNone
. - 8
-
Stampa del dizionario
d6
. Output:{'a': None, 'b': None, 'c': None}
.
13.2.7 None
None
è un oggetto predefinito in Python che rappresenta un valore nullo. Non ha metodi né altri attributi. None
può essere utilizzato per fare riferimento ad un oggetto qualsiasi, oppure quando si vuole indicare l’assenza di un oggetto.
Le funzioni restituiscono None
come risultato a meno che return
non sia seguito da elenco di oggetti. None
è hashable e può essere utilizzato come chiave di un dizionario. None
è un oggetto della classe NoneType
, che deriva da object
ed è unico, cioè non esiste una seconda istanza di NoneType
.
Esempi di utilizzo di None
:
- 1
-
Assegnazione di
None
a una variabile. - 2
-
Le funzioni che non hanno un’istruzione di ritorno specifica restituiscono
None
. Output:None
. - 3
-
None
può essere utilizzato come chiave in un dizionario. - 4
-
Output:
{None: 'valore'}
.
13.2.8 Ellissi
L’ellissi, scritta come tre punti consecutivi senza spazi intermedi (...
), è un oggetto speciale in Python utilizzato in applicazioni numeriche o come alternativa a None
quando None
è un valore valido.
Ad esempio, per inizializzare un dizionario che può accettare None
come valore legittimo, si può inizializzare con ...
come indicatore di “nessun valore fornito, neanche None
”. Ellipsis
è hashable e può essere utilizzato come chiave di un dizionario. Ellipsis
è un oggetto della classe ellipsis
, che deriva da object
ed è unica come None
.
Esempi di utilizzo di Ellipsis
:
1e = ...
2print(e)
3d = {None: "valore1", ...: "valore2"}
4print(d)
def funzione():
...
5print(funzione())
- 1
-
Assegnazione di
Ellipsis
a una variabile. - 2
-
Output:
Ellipsis
. - 3
-
Ellipsis
può essere utilizzato come chiave in un dizionario. - 4
-
Output:
{None: 'valore1', Ellipsis: 'valore2'}
. - 5
-
Le funzioni possono contenere
Ellipsis
come segnaposto per future implementazioni. Output:None
.