3. Introduzione a Python#
In questo capitolo saranno forniti alcuni concetti utili per l’analisi dei dati utilizzando Python come linguaggio di programmazione e Jupyter come ambiente di sviluppo. Tuttavia, verranno forniti solo brevi accenni sull’argomento poiché esistono numerose risorse online che approfondiscono questo tema. Per coloro che preferiscono una trattazione più completa, si consiglia il libro A Beginners Guide to Python 3 Programming di John Hunt (disponibile gratuitamente alla comunità UniFi). Il tutorial ufficiale della documentazione Python, in italiano, è fornito qui.
3.1. Cosa significa “programmare”?#
Python è un linguaggio di programmazione versatile adatto a una vasta gamma di scopi. È definito “general purpose” perché può essere utilizzato per sviluppare una varietà di applicazioni e risolvere diversi problemi. Python è noto per la sua leggibilità e per essere un linguaggio divertente da utilizzare.
Il nome “Python” è un omaggio ai famosi comici inglesi del gruppo Monty Python, il che aggiunge un tocco di eccentricità e creatività al linguaggio. Tuttavia, come accade con tutti i linguaggi di programmazione, richiede impegno, tempo ed esercizio per padroneggiarlo appieno. Nonostante la sua iniziale facilità di apprendimento, la comprensione dei concetti più avanzati e l’acquisizione delle competenze richiedono dedizione e pratica costante.
È importante comprendere fin dall’inizio che non è necessario memorizzare tutte le istruzioni e le regole sintattiche della programmazione. Gran parte del tempo trascorso come programmatore consiste nella ricerca online di soluzioni a problemi specifici, nel processo di debug per individuare errori e nella fase di testing del codice. Questo è un processo normale per programmatori di ogni livello di esperienza.
Il nostro obiettivo con questa guida è presentare i concetti fondamentali della programmazione anziché approfondire la sintassi precisa. Una volta compresi i concetti di base, è facile reperire informazioni dettagliate online. Pertanto, saper utilizzare gli strumenti di ricerca, come Google, è una delle competenze più importanti per ogni programmatore. Ricorda sempre che Google is your friend (anche se ovviamente si tratta di un’affermazione ironica).
3.2. Iniziare ad usare Python 🐍#
È possibile verificare la versione di Python in due modi.
Si può aprire il prompt dei comandi o il terminale ed eseguire
python --version
.Si può utilizzaree il modulo
sys
incorporato di Python.
import sys
print(sys.version)
3.11.3 (main, May 15 2023, 10:43:03) [Clang 14.0.6 ]
3.3. Espressioni e operatori#
I programmi sono composti da espressioni che combinano dati per istruire il computer. Ad esempio, l’espressione di moltiplicazione utilizza il simbolo * tra due numeri. Il computer valuta tali espressioni, come 3 * 4, e mostra il risultato (il valore) nell’espressione successiva di una cella nel notebook Jupyter.
Le regole sintattiche di un linguaggio di programmazione sono rigorose. In Python, non è consentito inserire due volte consecutive il simbolo *. Se un’espressione non segue la struttura corretta, il computer segnala un errore di sintassi (SyntaxError). La sintassi di un linguaggio rappresenta tutte le regole grammaticali e un SyntaxError indica che l’espressione viola le regole del linguaggio. Ad 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 x 3 x 3 x 3). In programmazione, i simboli come *
e **
sono chiamati “operatori” e i valori che operano su di essi sono chiamati “operandi”.
3 ** 4
81
La tabella seguente riassume i principali operatori binari utilizzati in Python.
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, il resto di 3/2 è 1. La divisione intera (scartando il resto) di 3/2 produce 1.
Usando gli operatori precedenti possiamo dunque 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
3.4. 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
I nomi devono iniziare con una lettera, ma possono contenere sia lettere che numeri. Un nome non può contenere uno spazio; invece, è comune utilizzare il carattere _
per sostituire ogni spazio. Sta al programmatore scegliere nomi facili da interpretare.
Possiao utilizzare il pacchetto incorporato “keyword” per ottenere la lista delle parole chiave di Python che non possono essere usate per i nomi di variabili.
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
3.5. I tipi dei dati#
Python è un linguaggio a tipizzazione dinamica. Ciò significa che in base al valore che assegniamo a una variabile, viene impostato il tipo di dati corrispondente.
Ogni valore ha un tipo e la funzione type()
restituisce il tipo del risultato di qualsiasi espressione.
type(b)
float
Di seguito sono riportati i più comuni tipi di dati.
Data type |
Mutabile? |
---|---|
None |
❌ |
bytes |
❌ |
bool |
❌ |
int |
❌ |
float |
❌ |
complex |
❌ |
str |
❌ |
tuple |
❌ |
list |
✅ |
set |
✅ |
dictionary |
✅ |
Dobbiamo naturalmente porci la domanda: “Cos’è Mutabile?”. La risposta è la seguente: se un oggetto può essere modificato dopo la sua creazione, allora è Mutabile; altrimenti, se non può essere modificato, è Immutabile.
3.5.1. Numeri#
Quando si lavora con numeri in programmazione, è importante tenere presente alcune considerazioni. Sebbene i computer siano principalmente utilizzati per eseguire calcoli numerici, in Python (come nella maggior parte degli altri linguaggi di programmazione) ci sono due tipi di numeri distinti: gli interi (int
) e i numeri in virgola mobile (float
).
Gli interi, rappresentati dal tipo int
, possono rappresentare solo numeri interi senza una parte frazionaria. Possono essere positivi, negativi o zero.
D’altra parte, i numeri in virgola mobile, chiamati float
, possono rappresentare sia numeri interi che numeri frazionari. Tuttavia, i float
hanno alcune limitazioni. Possono rappresentare solo la mantissa di un numero decimale con una precisione di circa 15 o 16 cifre. Oltre questo limite, la precisione viene persa. Nonostante questa limitazione, la maggior parte delle applicazioni viene gestita senza problemi.
Se si devono utilizzare numeri molto grandi o molto piccoli, è comune utilizzare la notazione scientifica, ad esempio m * 10^n
. Solitamente, il 10 viene omesso e l’esponente viene indicato con la lettera E
. Ad esempio, 1E9
rappresenta un miliardo e 1E-9
rappresenta un miliardesimo.
Il tipo di un numero può essere facilmente riconosciuto dalla sua rappresentazione. I valori int
non hanno un punto decimale, mentre i valori float
hanno sempre un punto decimale.
3
3
3.0
3.0
3.5.2. 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.
3.5.2.1. Metodi#
A partire da una stringa esistente, è possibile creare altre stringhe utilizzando i metodi applicabili alle stringhe. I metodi sono funzioni che operano sulle stringhe. Per chiamare un metodo, si aggiunge un punto dopo la stringa e si chiama la funzione corrispondente. Ad esempio, il seguente metodo genera una versione in maiuscolo di una stringa.
"loud".upper()
'LOUD'
3.5.3. Valori booleani e confronti#
Gli oggetti di tipo bool hanno solo due valori: True✅ e False❌.
Il corrispettivo intero di True è 1 e per False è 0. È dunque possibile eseguire le operazioni aritmetiche sui valori booleani: True equivale a 1 e False a 0. 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).
3.5.3.1. 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
3.5.3.2. Operatori Booleani#
Gli operatori booleani confrontano espressioni (non valori) e ritornano un valore booleano. 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.
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.
Questi operatori si comportano come ci possiamo aspettare.
True and False
False
True or False
True
not True
False
Altri 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
In Python, gli operatori logici and
, or
e not
sono utilizzati per combinare o invertire le condizioni booleane.
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.
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.
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.
Ecco alcuni esempi di come questi operatori possono essere utilizzati:
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
3.6. 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.
3.6.1. 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.
3.6.2. 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]). Si osservi che l’elemento ‘Pippo’ (una stringa) è racchiuso tra apici singoli (ma potrebbe anche essere racchiuso tra apici doppi).
Gli elementi nella lista sono ordinati in base all’indice, che 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']
3.6.3. 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
Senza dubbio, 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ù facile. Si consideri al seguente
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
3.6.4. 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]
3.6.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))
{'lemon', 'tomato', 'orange', 'mango', 'potato', 'banana', 'cabbage', 'onion', 'carrot'}
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
3.6.6. 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'}
3.6.7. 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()
.
3.7. Funzioni#
Lo scopo delle funzioni è raggruppare il codice in un formato organizzato, leggibile e riutilizzabile. Utilizzando le funzioni, è possibile ridurre la ridondanza del codice.
Una regola generale per le funzioni è che dovrebbero essere piccole e svolgere un’unica operazione.
Nella programmazione, una funzione accetta un input, esegue delle operazioni su di esso e può restituire un output. Python fornisce un’ampia gamma di funzioni integrate. È possibile anche importare funzioni da pacchetti aggiuntivi o definire nuove funzioni.
Consideriamo la definizione di una nuova funzione. In Python, si definisce una funzione utilizzando la parola chiave def
, seguita dal nome della funzione e dai nomi simbolici dei suoi argomenti, separati da virgole e racchiusi tra parentesi. La definizione continua con i due punti (:) e il corpo della funzione, le cui istruzioni devono essere indentate. Ciò che viene restituito dalla funzione è specificato utilizzando la parola chiave return
, di solito nella riga finale del corpo della funzione.
def add_numbers(a, b):
"""
Add two numbers together
Returns
-------
the_sum : type of arguments
"""
return a + b
Una volta definita una funzione, è possibile eseguirla chiamandola e passando gli argomenti appropriati. Ad esempio, possiamo chiamare la funzione add_numbers
per sommare due numeri, come ad esempio 20 e 10:
add_numbers(20, 10)
30
Consideriamo la funzione roll_die()
:
import random
def roll_die():
"""returns a random int between 1 and 6"""
return random.choice([1, 2, 3, 4, 5, 6])
Il corpo della funzione è composto da una singola riga di codice che utilizza la funzione choice()
della libreria random
, a cui viene passata una lista. Questo significa che una funzione può utilizzare altre funzioni che sono già state definite. In questo caso, la funzione si limita a specificare l’argomento da passare a choice()
. La funzione choice()
restituirà un numero casuale tra quelli specificati in input. Pertanto, la funzione roll_die()
simula il lancio di un dado:
roll_die()
3
roll_die()
1
Si noti inoltre la docstring
, cioè una stringa (in genere racchiusa tra “””…”””) che si trova come prima istruzione all’interno di una funzione. La docstring
contiene informazioni sullo scopo e sulle modalità d’uso della funzione.
3.7.1. Introspection#
Usando un punto interrogativo (?) prima o dopo una variabile è possibile visualizzare alcune informazioni generale su quell’oggetto. Nel caso di una funzione viene stampata la doc string
.
roll_die?
3.7.2. Metodi#
Le funzioni che sono definite all’interno di una classe, chiamate “metodi”, rappresentano operazioni specifiche che possono essere eseguite sugli oggetti di quella classe. Una classe è una struttura concettuale che rappresenta un concetto o un oggetto nel contesto del problema che stiamo affrontando. Prendendo ad esempio il capitolo sull’introduzione a Pandas, quando lavoriamo con dati organizzati in una tabella, utilizziamo un oggetto chiamato DataFrame che appartiene alla classe “pandas.DataFrame”. Un DataFrame è una struttura tabellare che contiene dati disposti in righe e colonne.
I metodi specifici per la classe DataFrame offrono funzionalità specifiche per manipolare e analizzare i dati in questa struttura. Ad esempio, possiamo utilizzare il metodo “hist()” per generare istogrammi dei valori presenti in una determinata colonna del DataFrame. Per invocare un metodo su un oggetto DataFrame, come ad esempio “df”, utilizziamo la sintassi “nome_oggetto.nome_metodo()”, e possiamo anche passare eventuali parametri richiesti dal metodo all’interno delle parentesi.
D’altra parte, gli attributi rappresentano le caratteristiche o le proprietà degli oggetti di una classe. Gli attributi possono essere richiamati utilizzando la sintassi “nome_oggetto.nome_attributo” e restituiscono un valore specifico associato a quell’oggetto. Ad esempio, l’attributo “.shape” applicato a un DataFrame come “df.shape” restituisce il numero di righe e colonne presenti nel DataFrame.
In sintesi, una classe definisce un tipo di oggetto che ha determinati attributi che ne descrivono le caratteristiche e metodi che rappresentano le azioni che possono essere eseguite su di esso. Gli attributi forniscono informazioni specifiche sull’oggetto, mentre i metodi consentono di effettuare operazioni e manipolazioni sui dati contenuti nell’oggetto stesso.
3.7.3. La funzione lambda
#
Python offre una sintassi alternativa che consente di definire funzioni “inline”, cioè in una singola linea di codice. Queste funzioni, chiamate funzioni anonime, non richiedono una definizione esplicita poiché vengono utilizzate solo nel punto in cui sono dichiarate. Per creare una funzione anonima, utilizziamo la parola chiave lambda
, seguita da un elenco di argomenti separati da virgole, due punti “:” e l’espressione che definisce il comportamento della funzione basandosi sugli argomenti forniti. Questa sintassi permette di creare funzioni semplici ed espressive in modo conciso.
Nell’esempio seguente, la funzione somma 1 al valore passato come input:
(lambda x : x + 1)(2)
3
In questo esempio sommiamo i due numeri in entrata:
(lambda x, y: x + y)(2, 3)
5
È anche possibile assegnare un nome alla funzione lambda:
myadd = lambda x, y: x + y
myadd(20, 10)
30
La sintassi seguente è valida in quanto, per l’interprete, il carattere _
corrisponde all’ultima funzione che è stata valutata:
lambda x, y: x + y
<function __main__.<lambda>(x, y)>
_(20, 10)
30
Si noti abbiamo valutato la funzione lambda x, y: x + y
in una cella precedente a quella che contiene _(20, 10)
; inserendo le due espressioni in una singola cella si ottiene un SyntaxError
.
3.7.4. Le funzioni map()
e filter()
#
Per gli esercizi che svolgeremo in seguito, risultano utili le funzioni map()
e filter()
.
La funzione map()
prende come input una funzione e una lista, e restituisce il risultato dell’applicazione della funzione a ciascun elemento della lista (è anche possibile usare qualsiasi oggetto iterabile al posto della lista). La lista stessa rimane invariata. Ad esempio, la seguente linea di codice eleva al quadrato ciascuno degli elementi della lista a
e salva il risultato nella lista b
:
a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
b = list(map(lambda x: x * x, a))
b
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
Un’altra funzione molto utile per manipolare gli oggetti iterabili è la funzione filter()
. Questa funzione filtra un oggetto iterabile selezionando solo gli elementi che rispondono ad un determinato predicato. (Il predicato è una funzione che restituisce un booleano). Per esempio
c = list(filter(lambda x: x > 50, b))
c
[64, 81, 100]
Sia map()
che filter()
restituiscono risultati che non sono ancora stati calcolati.
filter(lambda x: x > 50, b)
<filter at 0x1118ced10>
Possiamo visualizzare il risultato convertendolo in una lista:
list(filter(lambda x: x > 50, b))
[64, 81, 100]
3.7.5. La funzione zip()
#
La funzione zip()
crea una lista di tuple dagli elementi di due contenitori. Come nel caso delle operazioni precedenti, gli elementi vengono calcolati solo quando viene richiesto. Per esempio:
a = list(range(4))
a
[0, 1, 2, 3]
b = list(range(4, 8))
b
[4, 5, 6, 7]
b = zip(a, b)
b
<zip at 0x1114c2900>
list(b)
[(0, 4), (1, 5), (2, 6), (3, 7)]
3.8. Il flusso di esecuzione#
In Python il codice viene eseguito sequenzialmente, partendo dalla prima riga fino a quando non c’è più nulla da eseguire. L’ordine di esecuzione delle varie istruzioni è detto flusso di esecuzione.
Per esempio la cella seguente prima memorizza la lista names
, poi la lista born
e infine la lista dead
.
names = ["Sigmund Freud", "Jean Piaget", "Burrhus Frederic Skinner", "Albert Bandura"]
born = [1856, 1896, 1904, 1925]
dead = [1939, 1980, 1990, None]
Ho usato il valore speciale None
in quanto non risulta disponibile l’anno. In queste situazioni si parla di valori mancanti (missing values) che, di norma, vengono indicati con la sigla NA (not available).
La cella seguente include le istruzioni condizionali che specificano se e quando devono essere eseguiti determinati blocchi di codice. La più semplice istruzione di controllo è l’istruzione if
. Per esempio:
name = "Maria"
grade = 29
if name == "Maria" and grade > 28:
print("Maria, hai ottenuto un ottimo voto all'esame!")
if name == "Giovanna" or grade > 28:
print(
"Tu potresti essere Giovanna oppure potresti avere ottenuto un ottimo voto all'esame."
)
if name != "Giovanna" and grade > 28:
print("Tu non sei Giovanna ma hai ottenuto un ottimo voto all'esame.")
Maria, hai ottenuto un ottimo voto all'esame!
Tu potresti essere Giovanna oppure potresti avere ottenuto un ottimo voto all'esame.
Tu non sei Giovanna ma hai ottenuto un ottimo voto all'esame.
Tutte e tre le condizioni precedenti ritornano True
, quindi vengono stampati tutti e tre i messaggi.
Si noti che ==
e !=
confrontano valori, mentre is
e not
confrontano oggetti. Per esempio,
name_list = ["Maria", "Giovanna"]
name_list_two = ["Marco", "Francesco"]
# Compare values
print(name_list == name_list_two)
# Compare objects
print(name_list is name_list_two)
False
False
Una delle parole chiave condizionali più utili è in
. Un esempio è il seguente:
name_list = ["Maria", "Giovanna", "Marco", "Francesco"]
print("Giovanna" in name_list)
print("Luca" in name_list)
True
False
La condizione opposta è not in
.
print("Luca" not in name_list)
True
Facciamo un altro esempio.
age = 26
if age >= 18:
print("Sei maggiorenne")
Sei maggiorenne
Una struttura di selezione leggermente più complessa è “if-else”. La sintassi di questa struttura è la seguente:
if <condizione>:
<istruzione_se_condizione_vera>
else:
<istruzione_se_condizione_falsa>
La semantica di “if-else” è quella che ci si aspetta: la condizione tra la parola chiave if
e il carattere di due punti viene valutata: se risulta vera viene eseguita l’istruzione alla linea seguente, altrimenti viene eseguita l’istruzione dopo la parola chiave else
. Anche in questo caso l’indentazione permette di identificare quali istruzioni devono essere eseguite nei due rami della selezione. Per esempio:
age = 16
if age >= 18:
print("Sei maggiorenne")
else:
print("Sei minorenne")
Sei minorenne
In presenza di più di due possibilità mutuamente esclusive ed esaustive possiamo usare l’istruzione elif
. Per esempio:
cfu = 36
thesis_defense = False
if cfu >= 180 and thesis_defense == True:
print("Puoi andare a festeggiare!")
elif cfu >= 180 and thesis_defense == False:
print("Devi ancora superare la prova finale!")
else:
print("Ripassa tra qualche anno!")
Ripassa tra qualche anno!
3.8.1. Commenti#
In Python è possibile usare il carattere # per aggiungere commenti al codice. Ogni riga di commento deve essere preceduta da un #. I commenti non devono spiegare il metodo (cosa fa il codice: quello si vede), ma bensì lo scopo: quello che noi intendiamo ottenere. I primi destinatari dei commenti siamo noi stessi tra un po’ di tempo, ovvero quando ci saremo dimenticati cosa avevamo in mente quando abbiamo scritto il codice.
# This is a comment and will not be executed.
3.9. Cicli#
3.9.1. Il ciclo while
#
il ciclo while
permette l’esecuzione di un blocco di codice finché una determinata condizione è True. Per esempio:
counter = 0
while counter <= 10:
print(counter)
counter += 1
0
1
2
3
4
5
6
7
8
9
10
Il codice counter += 1
è equivalente a counter = counter + 1
e, ogni qualvolta viene eseguito il ciclo, riassegna alla variabile counter
il valore che aveva in precedenza + 1.
L’istruzione while
controlla se alla variabile counter
è associato un valore minore o uguale a 10. Nel primo passo del ciclo la condizione è soddisfatta, avendo noi definito counter = 0
, pertanto il programma entra nel loop, stampa il valore della variabile counter
e incrementa counter
di un’unità.
Questo comportamento si ripete finché la condizione counter <= 10
risulta True
. Quando il contatore counter
assume il valore 11 il ciclo while
si interrompe e il blocco di codice del ciclo non viene più eseguito.
3.9.2. Il ciclo for
#
Il ciclo for
è un costrutto di controllo di flusso che viene utilizzato per iterare su una sequenza di valori, come ad esempio una lista, una tupla, una stringa o un dizionario.
La sintassi generale di un ciclo for in Python è la seguente:
for element in sequence:
# codice da eseguire
Dove element
è una variabile temporanea che assume il valore di ciascun elemento della sequenza ad ogni iterazione del ciclo, e sequence
è la sequenza di valori su cui iterare.
Durante l’esecuzione del ciclo, il blocco di codice indentato sotto la linea for viene eseguito una volta per ogni elemento della sequenza. Ad ogni iterazione, la variabile elemento assume il valore dell’elemento corrente della sequenza e il codice all’interno del blocco viene eseguito con questo valore.
Il ciclo for
è spesso utilizzato per eseguire operazioni su ciascun elemento di una sequenza, come ad esempio la somma degli elementi di una lista o la stampa di ciascun carattere di una stringa. Per esempio
numbers = [0, 1, 2, 3, 4, 5]
for number in numbers: # number is temporary name to refer to the list's items, valid only inside this loop
print(number)
0
1
2
3
4
5
language = "Python"
for letter in language:
print(letter)
P
y
t
h
o
n
La funzione range()
è spesso usata nei cicli for
e permette di impostare un intervallo di esecuzione tanto ampio quanto il numero che le passiamo come parametro meno uno.
La funzione range()
prende tre parametri: start (default 0), stop e step (default 1), ovvero un punto di inizio dell’intervallo, un punto di fine e un passo di avanzamento. L’indicizzazione Python parte da 0; quindi range(0, 11, 1)
una lista di 11 elementi, da 0 a 10 inclusi.
print(list(range(0, 11, 1)))
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Ad esempio, impostiamo un punto di inizio a 3, il punto di fine a 11 e un passo di 2:
print(list(range(3, 12, 2)))
[3, 5, 7, 9, 11]
In un ciclo for
, l’intervallo di range()
corrisponde al numero di iterazioni che verranno eseguite, ovvero al numero di volte che il ciclo verrà processato. Nel caso seguente, l’indice del ciclo (qui chiamato number
) assume il valore 0 la prima volta che il ciclo viene eseguito e il valore 10 nell’ultima esecuzione del ciclo.
for number in range(11):
print(number)
0
1
2
3
4
5
6
7
8
9
10
for number in range(3, 12, 2):
print(number)
3
5
7
9
11
3.9.2.1. Cicli for
annidati#
Sono possibili i cicli for
annidati, vale a dire un ciclo posto all’interno del corpo di un altro (chiamato ciclo esterno). Al suo primo passo, il ciclo esterno mette in esecuzione quello interno che esegue il proprio blocco di codice fino alla conclusione. Quindi, al secondo passo, il ciclo esterno rimette in esecuzione quello interno. Questo si ripete finché il ciclo esterno non termina. Per esempio:
for i in range(4):
for j in range(4):
print((i, j))
(0, 0)
(0, 1)
(0, 2)
(0, 3)
(1, 0)
(1, 1)
(1, 2)
(1, 3)
(2, 0)
(2, 1)
(2, 2)
(2, 3)
(3, 0)
(3, 1)
(3, 2)
(3, 3)
3.9.2.2. Funzione enumerate
#
Consideriamo ora la funzione enumerate()
. Tale funzione crea una lista:
name_list = ["Maria", "Giovanna", "Marco", "Francesco"]
enum_lst = enumerate(name_list)
Visualizziamo la lista creata da enumerate()
:
list(enum_lst)
[(0, 'Maria'), (1, 'Giovanna'), (2, 'Marco'), (3, 'Francesco')]
Iterando sui membri di una lista passata come argomento, enumerate()
ritorna un’altra lista nella quale ciascun elemento è una lista di due elementi: il primo elemento è un indice sequenziale mentre il secondo elemento corrisponde a ciò che conteneva la lista originaria. Nel seguente ciclo for
, la funzione enumerate
crea un indice (i
) che corrisponde alla posizione degli elementi nella lista:
for i, name in enumerate(name_list):
print(f"Nella lista l'indice {i} è associato al nome {name}")
Nella lista l'indice 0 è associato al nome Maria
Nella lista l'indice 1 è associato al nome Giovanna
Nella lista l'indice 2 è associato al nome Marco
Nella lista l'indice 3 è associato al nome Francesco
Si noti la formattazione f-string. Le stringhe formattate, chiamate anche f-string, consentono di inserire delle espressioni Python in una stringa di testo, racchiudendole entro parentesi graffe. Nell’esempio precedente il testo all’interno delle virgolette viene visualizzato esattamente come digitato. Le parentesi graffe sono un segnaposto che contiene variabili Python. Una volta eseguito il codice la funzione print()
visualizza la parte letterale (il testo) insieme ai valori delle variabili a cui abbiamo fatto riferimento. Si noti anche che il ciclo fa simulteneamente riferimento a due iterabili: i
e name
.
3.9.2.3. Funzione zip
#
Un altro utile tipo di ciclo for
fa uso della funzione zip
. Possiamo pensare alla funzione zip
come a una cerniera lampo, che riunisce gli elementi di due diversi iteratori. Ecco un esempio:
first_names = ["Maria", "Marco", "Francesco", "Giovanna"]
last_names = ["Blu", "Giallo", "Bianco", "Nero"]
for forename, surname in zip(first_names, last_names):
print(f"{forename} {surname}")
Maria Blu
Marco Giallo
Francesco Bianco
Giovanna Nero
3.9.2.4. Modificare gli elementi di una lista#
Il ciclo for
è il modo più comune per scorrere gli elementi di una lista, come abbiamo visto in precedenza.
for name in first_names:
print(name)
Maria
Marco
Francesco
Giovanna
Questo approccio può essere usato se abbiamo solo bisogno di leggere gli elementi della lista. Nel ciclo seguente, ad esempio, leggiamo gli elementi d una lista per incrementare una variabile così da calcolare una somma.
numbers = [2, -4, 1, 6, 3]
total = 0
for num in numbers:
total += num
print(total)
8
Ma se vogliamo cambiare gli elementi di una lista l’approccio precedente non funziona e dobbiamo usare gli indici. Nell’esempio seguente, questo risultato viene ottenuto utilizzando le funzioni range
e len
:
numbers = [2, -4, 1, 6, 3]
for i in range(len(numbers)):
numbers[i] = numbers[i] * 2
print(numbers)
[4, -8, 2, 12, 6]
Nel codice seguente, la funzione len()
ritorna 5.
numbers = [2, -4, 1, 6, 3]
len(numbers)
5
Quindi, range(5)
produce la seguente sequenza iterabile:
list(range(5))
[0, 1, 2, 3, 4]
Questi sono gli indici che verranno usati nelle iterazioni del ciclo for
.
La prima volta che il ciclo viene eseguito, l’indice
i
vale 0 enumbers[i]
si riferisce al primo elemento della lista;la seconda volta che il ciclo viene eseguito,
i
vale 1 enumbers[i]
si riferisce al secondo elemento della lista;e così via.
L’istruzione di assegnazione nel corpo del ciclo for
usa i
per leggere il valore i-esimo della lista originale (a destra dell’uguale) e per assegnargli un nuovo valore (a sinistra dell’uguale).
3.9.2.5. Liste vuote#
Un ciclo for
su una lista vuota non esegue mai il corpo del ciclo:
for x in []:
print("This never happens.")
3.9.3. List comprehension#
Una list comprehension è un modo conciso di creare una lista. È un modo compatto per creare una nuova lista. Accade speso di dover creare una lista dove ciascun elemento è il risultato di un’operazione condotta sugli elementi di un’altra lista o di un iterabile; oppure, di dover estrarre gli elementi che soddisfano una certa condizione. Per esempio, supponiamo di volere sommare una costante ad una lista di numeri. Usando un ciclo for
possiamo procedere nel modo seguente (si noti l’uso della funzione append
):
new_list = []
k = 10
for x in range(10):
new_list.append(x + k)
new_list
[10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
Oppure, in maniera più semplice, possiamo usare una list comprehension:
new_list = [x + k for x in range(10)]
new_list
[10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
Una list comprehension è racchiusa tra parentesi quadre; contiene un’espressione, seguita da una clausola for
, seguita da zero o più clausole for
o if
. La sintassi è la seguente:
[ <expression> for item in iterable <if optional_condition> ]
Il risultato è una nuova lista costruita valutando l’espressione nel contesto delle clausole for
e if
che la seguono. Una list comprehension combina dunque un ciclo for
e (se necessario) una o più condizioni logiche in una singola riga di codice. Esaminiamo una variante dell’esempio precedente.
list1 = [1, 2, 3, 4, 5, 6]
print("list1:", list1)
list2 = [item + 1 for item in list1]
print("list2:", list2)
list1: [1, 2, 3, 4, 5, 6]
list2: [2, 3, 4, 5, 6, 7]
Si noti che la parola item
avrebbe potuto essere quasi qualsiasi stringa (in precedenza abbiamo usato x
). La possiamo immaginare con la seguente definizione: ...per ogni elemento in ...
. Nel seguente esempio, sommiamo 1 agli elementi di list1
solo se sono pari:
list3 = [item + 1 for item in list1 if item % 2 == 0]
print('list3:', list3)
list3: [3, 5, 7]
Facciamo un altro esempio usando range()
:
num_list = range(50, 60)
[1 + num for num in num_list]
[51, 52, 53, 54, 55, 56, 57, 58, 59, 60]
Qui selezioniamo solo i numeri pari (oltre allo zero):
[i for i in range(11) if i % 2 == 0]
[0, 2, 4, 6, 8, 10]
Specificando una condizione, possiamo cambiare il segno solo dei numeri dispari nella lista:
[-i if i % 2 else i for i in range(11)]
[0, -1, 2, -3, 4, -5, 6, -7, 8, -9, 10]
Possiamo anche eseguire più iterazioni simultaneamente:
[(i, j) for i in range(3) for j in range(4)]
[(0, 0),
(0, 1),
(0, 2),
(0, 3),
(1, 0),
(1, 1),
(1, 2),
(1, 3),
(2, 0),
(2, 1),
(2, 2),
(2, 3)]
In questo esempio vengono selezionati solo i nomi inclusi nella lista female_names
:
first_names = ["Maria", "Marco", "Francesco", "Giovanna"]
female_names = ["Alice", "Maria", "Giovanna", "Lisa"]
female_list = [name for name in first_names if name in female_names]
print(female_list)
['Maria', 'Giovanna']
Nel seguente esempio vengono estratte le prime tre lettere di ciascuno dei nomi che compongono una lista:
letters = [name[0:3] for name in first_names]
letters
['Mar', 'Mar', 'Fra', 'Gio']
Per estrarre l’ultimo carattere di una stringa usiamo [-1]
:
my_string = "barbablù"
my_string[-1]
'ù'
Possiamo dunque usare seguente list comprehension estrae gli ultimi tre caratteri di ciascun elemento della lista first_names
.
letters = [name[-3:] for name in first_names]
letters
['ria', 'rco', 'sco', 'nna']
3.10. Librerie e moduli#
3.10.1. Importare moduli#
I moduli (anche conosciuti come librerie in altri linguaggi) sono dei file usati per raggruppare funzioni e altri oggetti. Python include una lista estensiva di moduli standard (anche conosciuti come Standard Library), ma è anche possibile scaricarne o definirne di nuovi. Prima di potere utilizzare le funzioni non presenti nella Standard Library all’interno dei nostri programmi dobbiamo importare dei moduli aggiuntivi, e per fare ciò usiamo il comando import
.
L’importazione può riguardare un intero modulo oppure solo uno (o più) dei suoi elementi. Consideriamo per esempio la funzione mean
. Essa è disponibile nel modulo numpy
. L’istruzione import numpy
importa tutto il modulo numpy. Dopo che un modulo è stato importato, è possibile accedere a un suo generico elemento usando il nome del modulo, seguito da un punto e dal nome dell’elemento in questione. Ad esempio, numpy.mean()
.
Indicare il nome di un modulo per poter accedere ai suoi elementi ha spesso l’effetto di allungare il codice, diminuendone al contempo la leggibilità. È per questo motivo che è possibile importare un modulo specificando un nome alternativo, più corto. È quello che succede quando scriviamo l’istruzione import numpy as np
. In questo caso, l’istruzione precedente diventa np.mean()
.
I moduli più complessi sono organizzati in strutture gerarchiche chiamate package. La seguente cella importa il modulo pyplot
che è contenuto nel package matplotlib
(matplotlib è la libreria di riferimento in Python per la creazione di grafici).
import matplotlib.pyplot as plt
Qui di seguito sono descritte tutte le possibilità:
# import everything from library
import random
# call function by
random.random()
#import everything, but change name
import random as rnd
# call function by
rnd.random()
# select what to import from library
from random import random
#call function by
random()
# import everything from library
from random import *
# call function by
random()
0.7539478682207561
Nella cella seguente importo seaborn
con il nome sns
e uso le sue funzionalità per impostare uno stile e una palette di colori per la visualizzazione dei grafici.
import seaborn as sns
sns.set_theme()
sns.set_palette("colorblind")
Nell’esempio seguente calcolo la somma degli elementi della lista numerica primes
usando funzione sum()
contenuta nella libreria NumPy che ho importato con il nome di np
:
import numpy as np
primes = [1, 2, 3, 5, 7, 11, 13]
np.sum(primes)
42
Calcolo la media di primes
:
np.mean(primes)
6.0
Scrivo una nuova funzione per la media, \(\bar{x} = n^{-1}\sum_{i=1}^n x_i\):
def my_mean(x):
res = np.sum(x) / len(x)
return res
my_mean(primes)
6.0
Si noti che, nel corpo di una funzione, è possibile usare altre funzioni: qui, np.sum()
e len()
.
È sempre possibile usare la funzione di help
su una funzione:
help(sum)
Help on built-in function sum in module builtins:
sum(iterable, /, start=0)
Return the sum of a 'start' value (default: 0) plus an iterable of numbers
When the iterable is empty, return the start value.
This function is intended specifically for use with numeric values and may
reject non-numeric types.
In Visual Studio Code è sufficiente posizionare il cursore sul nome della funzione.
3.11. Formattazione del codice#
3.12. Watermark#
%load_ext watermark
%watermark -n -u -v -iv -w -p pytensor
Last updated: Sat Jun 17 2023
Python implementation: CPython
Python version : 3.11.3
IPython version : 8.12.0
pytensor: 2.12.2
seaborn : 0.12.2
sys : 3.11.3 (main, May 15 2023, 10:43:03) [Clang 14.0.6 ]
matplotlib: 3.7.1
numpy : 1.24.3
Watermark: 2.3.1