Python (1)#
Python è un linguaggio di programmazione versatile e facile da leggere, adatto per svariati usi. Anche se il suo nome è un tributo al gruppo comico Monty Python, imparare Python richiede tempo, pratica e impegno.
Questa guida si concentra sull’insegnamento dei principi di base della programmazione, anziché su dettagli tecnici o specifiche di sintassi. L’obiettivo è fornire agli studenti una base teorica solida. Con questa conoscenza, saranno più capaci di risolvere problemi specifici e cercare autonomamente dettagli tecnici.
Nel mondo attuale, in cui l’intelligenza artificiale e strumenti come ChatGPT rivestono un ruolo sempre più centrale, è fondamentale sviluppare la capacità di pensare in modo algoritmico. Questa competenza va oltre la mera programmazione e fornisce un approccio strutturato per risolvere problemi complessi. Nonostante gli algoritmi di intelligenza artificiale siano di grande utilità, una comprensione approfondita dei principi di programmazione rimane essenziale per interpretare, modificare o migliorare le soluzioni proposte da tali sistemi. In conclusione, anche nell’era dell’IA, il pensiero algoritmico e una solida comprensione dei fondamenti della programmazione sono indispensabili.
Espressioni e Operatori#
I programmi sono insiemi di espressioni che elaborano dati per fornire istruzioni specifiche al computer. Ad esempio, in Python, l’operazione di moltiplicazione si esegue utilizzando l’asterisco (*
) tra due numeri. Quando si incontra un’espressione come 3 * 4
, il computer la valuta e produce il risultato, che può essere visualizzato in una cella successiva di un notebook Jupyter.
Le regole sintattiche in un linguaggio di programmazione come Python sono stringenti. Ad esempio, non è consentito inserire due simboli asterisco in sequenza senza un operando intermedio. Qualora un’espressione violi queste norme sintattiche, il sistema ritornerà un “SyntaxError”, un errore che indica la non conformità alle regole del linguaggio. Per esempio
3 * * 4
restituisce:
Cell In[3], line 1
3 * * 4
^
SyntaxError: invalid syntax
Anche piccole modifiche in un’espressione possono cambiarne completamente il significato. Nell’esempio successivo, lo spazio tra i due asterischi *
è stato rimosso. Tuttavia, poiché gli asterischi compaiono tra due espressioni numeriche, l’espressione è corretta e indica l’elevamento a potenza del primo numero al secondo: 3 elevato alla quarta potenza (\(3 \times 3 \times 3 \times 3\)). In programmazione, simboli come *
e **
sono noti come “operatori”, mentre i valori su cui agiscono sono denominati “operandi”.
3 ** 4
81
La tabella seguente elenca i principali operatori binari utilizzati in Python, chiamati così perché agiscono su due operandi.
Operazione |
Operatore |
---|---|
addizione |
|
sottrazione |
|
moltiplicazione |
|
divisione (reale) |
|
divisione (intera; rimuove il resto) |
|
resto (modulo) |
|
elevamento a potenza |
|
Le due operazioni che potrebbero essere meno familiari sono %
(trova il resto di una divisione) e //
(esegui una divisione scartando il resto).
Per esempio, la divisione intera (scartando il resto) di 11/2 produce 5.
11 // 2
5
Il resto di 11/2 è 1.
11 % 2
1
Usando gli operatori che abbiamo elencato in precedenza possiamo usare Python come un calcolatore.
a = 4
b = 2
print("a + b is", a + b)
print("a - b is", a - b)
print("a * b is", a * b)
print("a / b is", a / b)
print("a ** b is", a**b)
print("9 % 4 is", 9 % 4)
print("9 // 4 is", 9 // 4)
a + b is 6
a - b is 2
a * b is 8
a / b is 2.0
a ** b is 16
9 % 4 is 1
9 // 4 is 2
L’applicazione degli operatori aritmetici in Python dipende dalle seguenti regole di precedenza degli operatori, che sono analoghe a quelle usate in algebra.
Le espressioni tra parentesi vengono valutate per prime.
Successivamente si valutano gli elevamenti a potenza.
In seguito, si valutano moltiplicazioni, divisioni e moduli.
Per ultime vengono valutate somme e sottrazioni.
1 + 2 * 3 * 4 * 5 / 6 ** 3 + 7 + 8 - 9 + 10
17.555555555555557
1 + 2 * (3 * 4 * 5 / 6) ** 3 + 7 + 8 - 9 + 10
2017.0
Assegnazione#
Gli esempi precedenti hanno fatto uso di dichiarazioni di assegnazione. In una dichiarazione di assegnazione, si specifica un nome seguito dal simbolo di uguale (=) e dall’espressione che si desidera assegnare a tale nome. L’operazione di assegnazione consiste nell’associare il valore dell’espressione a destra del simbolo di uguale al nome a sinistra. Da quel momento in poi, ogni volta che il nome viene utilizzato in un’espressione, il valore associato durante l’assegnazione viene utilizzato al suo posto.
a = 10
b = 20
a + b
30
a = 1/4
b = 2 * a
b
0.5
Nomi delle Variabili#
I nomi delle variabili in Python possono contenere caratteri alfanumerici da a-z, A-Z, 0-9 e alcuni caratteri speciali come _. I nomi delle variabili normali devono iniziare con una lettera. I nomi delle variabili non possono contenere uno spazio; invece, è comune utilizzare il carattere _
per sostituire ogni spazio. Sta al programmatore scegliere nomi facili da interpretare.
Per convenzione, i nomi delle variabili iniziano con una lettera minuscola, mentre i nomi delle classi iniziano con una lettera maiuscola.
Inoltre, ci sono una serie di parole chiave (keyword) in Python che non possono essere utilizzate come nomi di variabili. Queste parole chiave sono:
import keyword
print(*keyword.kwlist, sep="\n")
False
None
True
and
as
assert
async
await
break
class
continue
def
del
elif
else
except
finally
for
from
global
if
import
in
is
lambda
nonlocal
not
or
pass
raise
return
try
while
with
yield
Si presti attenzione alla parola chiave “lambda”, che potrebbe facilmente essere un nome di variabile naturale in un programma scientifico. Tuttavia, essendo una parola chiave, non può essere utilizzata come nome di variabile.
Tipologie di Dati in Python#
Nel linguaggio di programmazione Python, i dati possono essere classificati in diverse tipologie, ognuna con caratteristiche e utilizzi specifici.
Stringhe (String)#
Le stringhe sono sequenze di caratteri, utilizzate per rappresentare testo. In Python, le stringhe possono essere create utilizzando apici singoli (' '
), doppi (" "
) o tripli (''' '''
oppure """ """
) per delimitare il testo. Esempi di stringhe sono:
"Hello, world!"
'Beyonce-Lemonade.txt'
"lemonade"
Numeri Interi (Integer)#
I numeri interi rappresentano numeri senza una componente decimale. In Python, possono essere creati assegnando un valore senza parte decimale a una variabile. Esempio di un numero intero è:
age = 20
Numeri in Virgola Mobile (Float)#
I numeri in virgola mobile, o “float”, rappresentano numeri che hanno una componente decimale. Sono creati assegnando un valore con una parte decimale a una variabile. Esempio di un numero float è:
temperature = 36.4
Valori Booleani (Boolean)#
I valori booleani possono assumere solo due stati: vero (True
) o falso (False
). Sono utilizzati per rappresentare le condizioni logiche e sono ottenuti attraverso espressioni di confronto. Esempio di un valore booleano è:
is_raining = False
Tipizzazione Dinamica in Python#
Python è un linguaggio con tipizzazione dinamica, il che significa che il tipo di una variabile è determinato dal valore che le viene assegnato durante l’esecuzione del programma e non necessita di essere dichiarato esplicitamente.
Per identificare il tipo di una variabile o del risultato di un’espressione, Python mette a disposizione la funzione type()
. Questa funzione, quando chiamata con una variabile o un’espressione come argomento, restituisce il tipo di dati corrispondente.
Nell’esempio seguente il programma stamperà <class 'str'>
, indicando che x
è una variabile di tipo “stringa”.
x = "hello"
print(type(x))
<class 'str'>
age = 20
print(type(age))
<class 'int'>
temperature = 36.4
print(type(temperature))
<class 'float'>
Tipi di Dati in Python e Mutabilità#
In Python, i dati hanno un attributo distintivo: essere mutabili o immutabili. La mutabilità di un tipo di dato si riferisce alla capacità di modificare un oggetto dopo la sua creazione senza doverne creare uno nuovo.
Tipi di Dati Immutabili#
Esempi Immutabili:
int
,float
,bool
,str
,tuple
,bytes
,None
Caratteristiche: Non possono essere modificati dopo la loro creazione. Qualsiasi operazione che sembra modificarli in realtà ne crea una nuova istanza.
Implicazioni:
Sicurezza: Non ci sono effetti collaterali in altre parti del codice.
Efficienza: Ottimizzazioni possibili perché non possono essere modificate.
Utilizzo: Spesso usati come chiavi in dizionari o elementi in set, grazie alla loro immutabilità.
Esempi#
Stringhe (
str
):saluto = "Ciao" saluto_modificato = saluto.replace("C", "B") # Crea una nuova stringa "Biao"
saluto
rimane invariato.Tuple (
tuple
):tupla = (1, 2, 3) # tupla[0] = 4 # Questo genererebbe un errore
Non è possibile modificare gli elementi di una tupla.
Tipi di Dati Mutabili#
Esempi Mutabili:
list
,set
,dictionary
Caratteristiche: Possono essere modificati dopo la loro creazione. L’oggetto stesso cambia.
Implicazioni:
Flessibilità: Possono cambiare dimensione, contenuto.
Precauzione: Modifiche possono avere effetti collaterali in altre parti del codice.
Esempi#
Liste (
list
):lista = [1, 2, 3] lista.append(4) # Modifica la lista aggiungendo 4
lista
ora è[1, 2, 3, 4]
.Dizionari (
dictionary
):dizionario = {"chiave": "valore"} dizionario["nuova_chiave"] = "nuovo_valore" # Aggiunge una nuova coppia chiave-valore
dizionario
ora contiene anche"nuova_chiave": "nuovo_valore"
.
La comprensione di questi concetti è importante in Python, soprattutto quando si lavora con strutture dati complesse o si condividono oggetti tra diverse parti di un programma. È necessario considerare la mutabilità per prevenire effetti indesiderati e garantire l’efficienza del codice.
Dettagli su Alcune Tipologie di Dati Numerici#
Numeri Interi (int)#
Gli interi (indicati con int
) rappresentano numeri senza una componente decimale, possono essere sia positivi che negativi, oltre allo zero. Sono immutabili e non presentano un punto decimale nella loro rappresentazione.
Numeri in Virgola Mobile (float)#
I numeri in virgola mobile, o “float”, possono rappresentare sia valori interi che frazionari, ed includono sempre un punto decimale. Tuttavia, hanno una precisione limitata a circa 15-16 cifre decimali; oltre questo limite, la precisione viene persa. Nonostante questa limitazione, sono sufficienti per la maggior parte delle applicazioni.
Inoltre, è possibile utilizzare la notazione scientifica per rappresentare numeri molto grandi o molto piccoli. In questa notazione, m * 10^n
viene comunemente abbreviato come mEn
, dove “E” rappresenta l’esponente dieci. Ad esempio, 1E9
equivale a un miliardo (\(1 \times 10^9\)) e 1E-9
rappresenta un miliardesimo (\(1 \times 10^{-9}\)).
Stringhe#
Gli oggetti di tipo stringa contengono una sequenza di caratteri.
Si noti il risultato ottenuto quando si applica l’operatore +
a due stringhe.
"data" + "science"
'datascience'
"data" + " " + "science"
'data science'
Sia le virgolette singole che doppie possono essere utilizzate per creare le stringhe: “ciao” e ‘ciao’ sono espressioni equivalenti. Tuttavia, le virgolette doppie sono spesso preferite poiché consentono di includere virgolette singole all’interno delle stringhe.
"Che cos'è una parola?"
"Che cos'è una parola?"
L’espressione precedente avrebbe prodotto un SyntaxError
se fosse stata racchiusa da virgolette singole.
Parsing strings#
In Python, una stringa è concepita come una sequenza ordinata di caratteri. Grazie all’operatore di indicizzazione, rappresentato dalle parentesi quadre []
, è possibile accedere a singoli elementi della stringa. L’indice del primo carattere è [0]
, quello del secondo è [1]
, del terzo [2]
, e così via. Questa funzionalità consente di manipolare o consultare specifici segmenti della stringa, piuttosto che gestirla come un blocco unico.
Consideriamo questo verso di Eugenio Montale:
my_string = "Tendono alla chiarità le cose oscure"
print(my_string)
Tendono alla chiarità le cose oscure
my_string[0]
'T'
my_string[3]
'd'
len(my_string)
36
La stringa “my_string” conta 36 caratteri, ma è importante ricordare che l’indicizzazione in Python parte da zero. Pertanto, gli indici validi per questa stringa vanno da 0 a 35. Per accedere all’ultimo carattere, è necessario utilizzare l’indice 35, che corrisponde a 36 meno 1. Un modo efficiente per ottenere l’ultimo carattere è ricorrere alla funzione len
, sottraendo 1 al risultato:
my_string[len(my_string)-1]
'e'
Slicing strings#
Oltre a estrarre caratteri individuali da una stringa, Python offre la possibilità di selezionare segmenti di testo attraverso la tecnica dello “slicing”. Questo meccanismo è simile all’indicizzazione, ma utilizza due indici separati da un carattere a due punti (:). Il primo indice indica la posizione di partenza dello “slicing” nella stringa, mentre il secondo indice segnala il punto in cui terminare l’estrazione del segmento.
my_string[2:4]
'nd'
Se si omette il primo indice, Python utilizzerà l’inizio della stringa; se si omette il secondo, utilizzerà la fine della stringa.
my_string[:4]
'Tend'
my_string[4:]
'ono alla chiarità le cose oscure'
Metodi#
A partire da una stringa esistente, si possono generare nuove stringhe mediante l’utilizzo di metodi specifici per le stringhe. Questi metodi sono essenzialmente funzioni che agiscono direttamente sull’oggetto stringa. Per invocare un metodo, basta posizionare un punto subito dopo la stringa e seguire con il nome del metodo desiderato. Ad esempio, il metodo successivo converte tutti i caratteri della stringa in maiuscole.
my_string.upper()
'TENDONO ALLA CHIARITÀ LE COSE OSCURE'
Il metodo my_string.title()
è utilizzato per convertire la prima lettera di ogni parola nella stringa my_string
in maiuscolo, mentre rende tutte le altre lettere minuscole. In pratica, trasforma la stringa in una forma “a titolo”, in cui ogni parola inizia con una lettera maiuscola.
my_string.title()
'Tendono Alla Chiarità Le Cose Oscure'
Valori Booleani e Confronti#
Gli oggetti booleani possono assumere solo uno dei due valori possibili: True
o False
.
Nel contesto delle operazioni aritmetiche, True
è equivalente al numero intero 1, mentre False
corrisponde a 0. Questo permette di includere valori booleani in calcoli matematici. Per esempio:
True + True + False
2
Un valore booleano viene ritornato quando si valuta un confronto. Per esempio:
3 > 1 + 1
True
Il valore True indica che il confronto è valido; Python ha confermato questo semplice fatto sulla relazione tra 3 e 1+1.
Si noti la regola di precedenza: gli operatori >, <, >=, <=, ==, != hanno la precedenza più bassa (vengono valutati per ultimi), il che significa che nell’espressione precedente viene prima valutato (1 + 1) e poi (3 > 2).
Operatori di confronto#
Un operatore di confronto è un operatore che esegue un qualche tipo di confronto e restituisce un valore booleano (True oppure False). Per esempio, l’operatore ==
confronta le espressioni su entrambi i lati e restituisce True
se hanno gli stessi valori e False
altrimenti. L’opposto di ==
è !=
, che si può leggere come ‘non uguale al valore di’. Gli operatori di confronto sono elencati qui sotto:
Confronto |
Operatore |
---|---|
Minore |
< |
Maggiore |
> |
Minore o uguale |
<= |
Maggiore o uguale |
>= |
Uguale |
== |
Non uguale |
!= |
Ad esempio:
a = 4
b = 2
print("a > b", "is", a > b)
print("a < b", "is", a < b)
print("a == b", "is", a == b)
print("a >= b", "is", a >= b)
print("a <= b", "is", a <= b)
a > b is True
a < b is False
a == b is False
a >= b is True
a <= b is False
Nella cella seguente si presti attenzione all’uso di =
e di ==
:
boolean_condition = 10 == 20
print(boolean_condition)
False
L’operatore =
è un’istruzione di assegnazione. Ovvero, crea un nuovo oggetto. L’operatore ==
valuta invece una condizione logica e ritorna un valore booleano.
Un’espressione può contenere più confronti e tutti devono essere veri affinché l’intera espressione sia vera. Ad esempio:
1 < 1 + 1 < 3
True
Operatori Booleani#
Gli operatori booleani (o operatori logici) confrontano espressioni (non valori) e ritornano un valore booleano. Python ha tre operatori logici:
and
– Ritorna True solo se entrambi le espressioni sono vere, altrimenti ritorna Falseor
– Ritorna True se almeno una delle due espressioni è vera, altrimenti ritorna False.not
– Ritorna True se l’espressione è falsa, altrimenti ritorna False.
Ad esempio:
a = 2
b = 3
(a + b > a) and (a + b > b)
True
Nella cella sopra le parentesi tonde sono opzionali ma facilitano la lettura.
L’operatore and
restituisce True
solo se entrambe le condizioni booleane sono vere. Ad esempio, True and False
restituirà False
perché una delle condizioni è falsa:
True and False
False
L’operatore or
restituisce True
se almeno una delle due condizioni booleane è vera. Ad esempio, True or False
restituirà True
perché almeno una delle condizioni è vera.
True or False
True
L’operatore not
viene utilizzato per invertire il valore di verità di una condizione booleana. Ad esempio, not True
restituirà False
e not False
restituirà True
.
not True
False
Alcuni esempi sono i seguenti (si noti l’uso della funzione len()
):
print(3 > 2) # True, because 3 is greater than 2
print(3 >= 2) # True, because 3 is greater than 2
print(3 < 2) # False, because 3 is greater than 2
print(2 < 3) # True, because 2 is less than 3
print(2 <= 3) # True, because 2 is less than 3
print(3 == 2) # False, because 3 is not equal to 2
print(3 != 2) # True, because 3 is not equal to 2
print(len("mango") == len("avocado")) # False
print(len("mango") != len("avocado")) # True
print(len("mango") < len("avocado")) # True
print(len("milk") != len("meat")) # False
print(len("milk") == len("meat")) # True
print(len("tomato") == len("potato")) # True
print(len("python") > len("dragon")) # False
True
True
False
True
True
False
True
False
True
True
False
True
True
False
Altri esempi di come questi operatori possono essere utilizzati sono i seguenti:
print(3 > 2 and 4 > 3) # True - because both statements are true
print(3 > 2 and 4 < 3) # False - because the second statement is false
print(3 < 2 and 4 < 3) # False - because both statements are false
print("True and True: ", True and True)
print(3 > 2 or 4 > 3) # True - because both statements are true
print(3 > 2 or 4 < 3) # True - because one of the statements is true
print(3 < 2 or 4 < 3) # False - because both statements are false
print("True or False:", True or False)
print(not 3 > 2) # False - because 3 > 2 is true, then not True gives False
print(not True) # False - Negation, the not operator turns true to false
print(not False) # True
print(not not True) # True
print(not not False) # False
True
False
False
True and True: True
True
True
False
True or False: True
False
False
True
True
False
Abbiamo tralasciato alcuni operatori in Python. Due di quelli che abbiamo omesso sono gli operatori di appartenenza, in
e not in
. Gli altri operatori che abbiamo tralasciato sono gli operatori bitwise e gli operatori sugli insiemi, che verranno trattati in seguito.
Valori Numerici di True e False#
È fondamentale comprendere i valori numerici associati alle parole chiave True
e False
. Queste due parole chiave hanno i valori numerici di 1 e 0, rispettivamente.
True == 1
True
False == 0
True
True + False
1
type(True + False)
int
Sequenze#
Oltre ai numeri e ai valori booleani, Python supporta anche un insieme di “contenitori”, ovvero i seguenti tipi strutturati:
le liste,
le tuple,
gli insiemi,
i dizionari.
Le tuple#
Una tupla è una collezione di diversi tipi di dati che è ordinata e immutabile (non modificabile). Le tuple sono scritte tra parentesi tonde, (). Una volta creata una tupla, non è possibile modificarne i contenuti.
colors = ("Rosso", "Nero", "Bianco")
colors
('Rosso', 'Nero', 'Bianco')
type(colors)
tuple
Le stringhe sono tuple di caratteri. Pertanto non sono modificabili.
Le liste#
Gli oggetti di tipo lista sono simili alle tuple, ma con alcune differenze. La lista è un oggetto mutabile, il che significa che possiamo aggiungere o rimuovere elementi dalla lista anche dopo la sua creazione. Una lista viene creata separando i suoi elementi tramite virgola e racchiudendo il tutto tra parentesi quadre.
Si noti che una lista è una struttura dati eterogenea contentente una sequenza di elementi che possono essere di tipo diverso.
my_list = ["Pippo", 3, -2.953, [1, 2, 3]]
my_list
['Pippo', 3, -2.953, [1, 2, 3]]
type(my_list)
list
La lista my_list
è composta da diversi elementi: una stringa (“Pippo”), un numero intero (3), un numero decimale (-2.953) e un’altra lista ([1, 2, 3]).
Gli elementi nella lista sono ordinati in base all’indice, il quale rappresenta la loro posizione all’interno della lista. Gli indici delle liste partono da 0 e aumentano di uno. Per accedere a un elemento della lista tramite il suo indice, si utilizza la notazione delle parentesi quadre: nome_lista[indice]
. Ad esempio:
my_list[1]
3
my_list[0]
'Pippo'
Python prevede alcune funzioni che elaborano liste, come per esempio len
che restituisce il numero di elementi contenuti in una lista:
len(my_list)
4
Benché questa lista contenga come elemento un’altra lista, tale lista nidificata conta comunque come un singolo elemento. La lunghezza di di my_list
è quattro.
Una lista vuota si crea nel modo seguente:
empty_list = []
len(empty_list)
0
Ecco alcuni esempi.
fruits = ["banana", "orange", "mango", "lemon"] # list of fruits
vegetables = ["Tomato", "Potato", "Cabbage", "Onion", "Carrot"] # list of vegetables
print("Fruits:", fruits)
print("Number of fruits:", len(fruits))
print("Vegetables:", vegetables)
print("Number of vegetables:", len(vegetables))
Fruits: ['banana', 'orange', 'mango', 'lemon']
Number of fruits: 4
Vegetables: ['Tomato', 'Potato', 'Cabbage', 'Onion', 'Carrot']
Number of vegetables: 5
Supponiamo di voler ordinare in ordine alfabetico i nomi presenti nella lista. Per fare ciò, è necessario utilizzare il metodo sort
sulla lista utilizzando la notazione con il punto (dot notation):
names = ["Carlo", "Giovanni", "Giacomo"]
names.sort()
Tale metodo però non restituisce alcun valore, in quanto l’ordinamento è eseguito in place: dopo l’invocazione, gli elementi della lista saranno stati riposizionati nell’ordine richiesto. Visualizziamo la listra trasformata:
names
['Carlo', 'Giacomo', 'Giovanni']
L’invocazione di metodi (e di funzioni) prevede anche la possibilità di specificare degli argomenti opzionali. Per esempio:
names.sort(reverse=True)
names
['Giovanni', 'Giacomo', 'Carlo']
Il metodo remove()
può essere usato per rimuovere elementi da una lista.
print(fruits)
fruits.remove("banana")
print(fruits)
['banana', 'orange', 'mango', 'lemon']
['orange', 'mango', 'lemon']
Il metodo insert()
può essere usato per aggiungere elementi ad una lista.
print(fruits)
fruits.insert(2, "watermelon")
print(fruits)
['orange', 'mango', 'lemon']
['orange', 'mango', 'watermelon', 'lemon']
È possibile copiare una lista in una nuova variabile:
print(fruits)
new_fruits = fruits.copy()
['orange', 'mango', 'watermelon', 'lemon']
print(new_fruits)
['orange', 'mango', 'watermelon', 'lemon']
Operazioni su liste#
L’operatore +
concatena liste:
a = [1, 2, 3]
b = [4, 5, 6]
c = a + b
print(c)
[1, 2, 3, 4, 5, 6]
In maniera simile, l’operatore *
ripete una lista un certo numero di volte:
[0] * 4
[0, 0, 0, 0]
[1, 2, 3] * 3
[1, 2, 3, 1, 2, 3, 1, 2, 3]
L’aspetto importante da considerare è che, essendo una sequenza di elementi eterogenei, è difficile eseguire operazioni algebriche sulle liste in Python puro. Ad esempio, consideriamo la seguente lista:
x = [1, 2, 3]
x
[1, 2, 3]
Se desideriamo calcolare una semplice operazione, come la media di x
, è necessario seguire una procedura abbastanza articolata. Ad esempio:
total = 0
counter = 0
for num in x:
counter += 1
total += num
avg = total / counter
print(avg)
2.0
Indubbiamente, sarebbe preferibile ottenere questo risultato con un approccio più semplice. In seguito, vedremo che se utilizziamo una sequenza di elementi omogenei, il problema può essere risolto in modo molto più agevole. Ad esempio,
import numpy as np
x = np.array([1, 2, 3])
np.mean(x)
2.0
Possiamo contare il numero degli elementi specificati che sono contenuti in una lista usando count()
.
ages = [22, 19, 24, 25, 26, 24, 25, 24]
print(ages.count(24))
3
Possiamo trovare l’indice di un elemento in una lista con index()
.
ages.index(24) # index of the first occurrence
2
Operatore slice#
L’operatore di slice (:) applicato alle liste in Python consente di estrarre una porzione specifica di elementi dalla lista. L’operatore di slice ha la seguente sintassi: lista[inizio:fine:passo]
.
inizio
rappresenta l’indice di partenza dell’intervallo (inclusivo).fine
rappresenta l’indice di fine dell’intervallo (esclusivo).passo
rappresenta il passo o l’incremento tra gli indici degli elementi selezionati (facoltativo).
Ecco alcuni esempi per illustrare l’utilizzo dell’operatore di slice:
lista = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
# Estrarre una porzione della lista
porzione = lista[2:6] # [3, 4, 5, 6]
# Estrarre una porzione con un passo specifico
porzione_passo = lista[1:9:2] # [2, 4, 6, 8]
# Estrarre una porzione dalla fine della lista
porzione_fine = lista[6:] # [7, 8, 9, 10]
# Estrarre una porzione dall'inizio della lista
porzione_inizio = lista[:5] # [1, 2, 3, 4, 5]
Gli insiemi#
Gli insiemi sono collezioni finite di elementi distinti e non memorizzati in un ordine specifico. Un insieme non può contenere più di un’istanza dello stesso elemento. Per creare un insieme si utilizzano le parentesi graffe {}. Ad esempio:
my_set = {"A", "B", "C", "D", "E", "F"}
my_set
{'A', 'B', 'C', 'D', 'E', 'F'}
type(my_set)
set
Gli oggetti di tipo “set” sono utili per eseguire operazioni matematiche sugli insiemi.
Per verificare se un elemento esiste in un insieme usiamo l’operatore in
.
print("Does set my_set contain D? ", "D" in my_set)
Does set my_set contain D? True
L’unione di due insieme si ottiene con union()
.
fruits = {"banana", "orange", "mango", "lemon"}
vegetables = {"tomato", "potato", "cabbage", "onion", "carrot"}
print(fruits.union(vegetables))
{'carrot', 'tomato', 'banana', 'onion', 'lemon', 'orange', 'mango', 'cabbage', 'potato'}
L’intersezione di due insieme si trova con intersection()
.
python = {"p", "y", "t", "h", "o", "n"}
dragon = {"d", "r", "a", "g", "o", "n"}
python.intersection(dragon)
{'n', 'o'}
Un insieme può essere un sottoinsieme o un sovrainsieme di altri insiemi.
Per verificare se un insieme è un sottoinsieme di un altro, si utilizza il metodo issubset()
.
Per verificare se un insieme è un sovrainsieme di un altro, si utilizza il metodo issuperset()
.
st1 = {"item1", "item2", "item3", "item4"}
st2 = {"item2", "item3"}
st2.issubset(st1)
True
st1.issuperset(st2)
True
La differenza tra due insiemi si ottiene con difference()
.
whole_numbers = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
even_numbers = {0, 2, 4, 6, 8, 10}
whole_numbers.difference(even_numbers)
{1, 3, 5, 7, 9}
Possiamo verificare se due insiemi sono disgiunti, ovvero non hanno elementi in comune, utilizzando il metodo isdisjoint()
.
st1 = {"item1", "item2", "item3", "item4"}
st2 = {"item2", "item3"}
st2.isdisjoint(st1)
False
I dizionari#
Gli oggetti di tipo “dizionario” vengono utilizzati per creare coppie chiave-valore, dove ogni chiave è unica. Un dizionario viene creato specificando ogni coppia come chiave : valore
, separando le diverse coppie con una virgola e racchiudendo il tutto tra parentesi graffe. Ad esempio:
music = {
"blues": "Betty Smith",
"classical": "Gustav Mahler",
"pop": "David Bowie",
"jazz": "John Coltrane",
}
L’accesso agli elementi di un dizionario viene fatto specificando all’interno di parentesi quadre la chiave per ottenere o modificare il valore corrispondente:
music["pop"]
'David Bowie'
Per trovare il numero di coppie key: value
nel dizionario usiamo len()
.
print(len(music))
4
music["new music"] = "Missy Mazzoli"
print(music)
{'blues': 'Betty Smith', 'classical': 'Gustav Mahler', 'pop': 'David Bowie', 'jazz': 'John Coltrane', 'new music': 'Missy Mazzoli'}
Contenitori vuoti#
A volte è utile creare dei contenitori vuoti. I comandi per creare liste vuote, tuple vuote, dizionari vuoti e insiemi vuoti sono rispettivamente lst = []
, tup=()
, dic={}
e st = set()
.
Watermark#
Alla fine di ogni capitolo e, in effetti, alla fine (o all’inizio) di qualsiasi notebook che creiamo, è utile includere informazioni sull’ambiente di calcolo, compresi i numeri di versione di tutti i pacchetti che utilizziamo. Il pacchetto watermark
può essere usato per questo scopo. Il pacchetto watermark
contiene comandi speciali ed è un’estensione di IPython. In generale, per utilizzare tali comandi speciali, li precediamo con il segno % o %% in una cella. Utilizziamo la funzione speciale built-in %load_ext
per caricare watermark
, e quindi utilizziamo %watermark
per invocarlo.
%load_ext watermark
%watermark -n -u -v -iv -w -m
Last updated: Sat Feb 03 2024
Python implementation: CPython
Python version : 3.11.7
IPython version : 8.19.0
Compiler : Clang 16.0.6
OS : Darwin
Release : 23.3.0
Machine : x86_64
Processor : i386
CPU cores : 8
Architecture: 64bit
numpy: 1.26.2
Watermark: 2.4.3
Ecco una spiegazione dettagliata delle opzioni che sono state utilizzate nell’istruzione precedente.
-n
o--datename
: Aggiunge la data e l’ora correnti al watermark. Questo può essere utile per mantenere una cronologia delle modifiche o delle esecuzioni del notebook.-u
o--updated
: Mostra l’ultima volta in cui il notebook è stato salvato. È utile per tenere traccia delle modifiche recenti apportate al notebook.-v
o--python
: Mostra la versione di Python utilizzata nel kernel del notebook. Questo è importante per garantire la compatibilità del codice e replicare gli ambienti di lavoro.-iv
o--iversions
: Visualizza le versioni delle librerie importate nel notebook. È fondamentale per la replicabilità degli esperimenti e degli analisi, dato che diverse versioni delle librerie possono comportare risultati diversi.-w
o--watermark
: Aggiunge il watermark stesso, che è semplicemente il logo “watermark”. È più una questione estetica che funzionale.-m
o--machine
: Fornisce informazioni sulla macchina su cui viene eseguito il Jupyter Notebook, come il tipo di sistema operativo e l’architettura della macchina (ad esempio, x86_64). Questo può essere utile per documentare l’ambiente hardware in cui vengono eseguiti gli esperimenti.
Queste opzioni forniscono un modo semplice e immediato per documentare e tracciare importanti metadati nei notebook Jupyter.