Per eseguire comandi di Python, noi useremo una interfaccia potente, il Jupyter Notebook. Il notebook è diviso in celle. I due tipi di celle che ci interessano sono il tipo Code
dove si possono scrivere ed eseguire comandi, e il tipo Markdown
che contengono testo.
Mescolare celle eseguibili e celle di testo in cui spiegare il procedimento che si sta
utilizzando oppure dare informazioni per rendere comprensibile il codice permette di creare dei documenti interattivi.
Lo stile delle celle di testo può essere specificato usando comandi Markdown e/o Latex.
Blocchi di codice hanno di fronte il prompt In[#]
oppure [#]
, dove #
è un intero progressivo che identifica la cella.
Per eseguire la cella, schiacciate Shift-Enter (Maiusc-Invio).
4 + 5
In una cella ci possono essere più comandi, ognuno sulla propria linea. Il risultato dell'ultimo comando viene scritto sullo schermo (a meno che venga assegnato a una variabile. Ne parleremo più tardi).
4 + 5
7 + 33
Per stampare i risultati delle operazioni precedenti l'ultima possiamo usare print
:
print(4 + 5)
7 + 33
Le operazioni elementari come l'addizione (+
), sottrazione (-
), moltiplicazione (*
), divisione (/
) e esponenziazione (**
) funzionano come ci si aspetta.
10 + 10000
42 - 1.5
47 * 11
10 / 0.5
2**2
2**4
Usando il fatto che $\sqrt[n]{x} = x^{1/n}$, possiamo calcolare $\sqrt{3} = 1.732050\dots$ con il simbolo **
:
3**0.5
All'interno delle celle di tipo Code
è possibile inserire commenti (che vengono ignorati quando la cella viene eseguita).
# Questo è un commento
2 + 2
2 + 2 # un commento sulla stessa di un comando
a < b
significa che a
viene eseguita prima di b
:
2**4/3 # Interpretato come (2**4)/3, non come 2**(4/3)
Le parentesi tonde (e solo quelle tonde) servono per raggruppare:
2 * 10 + 5
2 * (10 + 5)
2**(4/3)
Sono funzioni sempre disponibili in Python. Per esempio, le funzioni che trasformano una variabile da un tipo ad un altro (Si veda 03_Tipi_di_Dati.ipynb) oppure max
, min
,abs
. La lista completa delle funzioni intrinseche si trova in Built-in functions.
Qualche esempio:
Se a
e b
sono interi, a % b
fornisce il modulo di a
rispetto a b
cioè il resto della divisione di a
per b
.
8 % 5
100 % 9
Se a
e b
sono interi, a // b
fornisce la parte intera di a/b
.
9//2
23//8
La quasi totalità delle funzioni in Python sono contenute in librerie dette moduli che devono essere importati per poter utilizzare le funzioni al loro interno. Vanno importate solo le librerie che vengono utilizzate.
Le funzioni matematiche più comuni come seno (sin), coseno (cos), esponenziale (exp), logaritmo (log) e molte altre si trovano in un modulo chiamato math
. Per utilizzare le funzioni dobbiamo importare il modulo:
import math
math.exp(1.0)
Usando la funzione dir
, possiamo vedere la lista dei metodi e funzioni disponibili in un modulo:
dir(math)
La funzione help
fornisce informazioni più dettagliate:
help(math)
help(nome_modulo.nome_funzione)
fornisce informazioni sulla funzione nome_funzione
contenuta nel modulo nome_modulo
:
help(math.exp)
help(nome_variabile)
mostra tutti i metodi e funzioni che si possono far agire sulla variabile:
a = 37.79
help(a)
Se le informazioni fornite da dir
e help
non sono sufficientemente dettagliate o chiare, internet viene in soccorso. Cercate "python math" nel browser.
Il modulo math
definisce le costanti π
e e
:
math.pi
math.e
math.cos(math.pi) # L'argomento delle funzioni trigonometriche è in radianti
math.log(math.e) # La funzione log ha base "e"
Una variabile può essere usare per immagazzinare il valore di un oggetto. In Python, tutti i numeri (e tutto il resto, incluse funzioni moduli e files) sono oggetti. Una variabile viene creata attraverso una assegnazione. Per assegnare il valore a una variabile si usa il segno (’=’).
La struttura di una assegnazione è sempre: nome_variabile = valore
.
I nomi delle variabili possono contenere solo i caratteri a-z, A-Z, 0-9 e l'underscore "".
Il primo carattere del nome di una variabile non può essere un numero. `a1bè legale;
x+1,
1ae
a(cd` non lo sono.
x = 0.5
Una volta che la variabile x
è stata creata assegnandogli il valore 0.5, come nell'esempio, possiamo utilizzarla in ulteriori comandi:
x*3
x**2
width = 20
height = 5 * 9
area = width * height
print("Area =",area)
Quando a una variabile viene assegnato un nuovo valore il contenuto della memoria viene sovrascritto, il valore precedente viene perso:
width = 15.7
print("width =",width)
Quando area
viene calcolata, solo il suo valore viene salvato in memoria. Se si vuole che area
rifletta il nuovo valore di width
è necessario ricalcolarla.
print("Area =",area)
area = width * height
print("New Area =",area)
È possibile assegnare un valore a più variabili contemporaneamente:
x = y = z = 0 # inizializza x, y e z a 0
x
y
z
Le variabili devono essere create (assegnando loro un valore) prima di poterle utilizzare, altrimenti si ha un errore :
# Tentativo di accedere ad una variabile non definita
n
Potete trovare una buona introduzione a come interpretare i messaggi di errore di Python in https://realpython.com/python-traceback/. È utile consultarla regolarmente. Ovviamente è in inglese.
Python è "case sensitive". var
, Var
e vAr
sono variabili diverse:
Var = 1
var = 33
print('Var:',Var)
print('var:',var)
Per essere precisi, quando scrivete
x = 0.5
questo è quello che succede.
Per prima cosa, Python crea l'oggetto 0.5
. Tutto in Python è un oggetto, e quindi lo è anche il numero reale (floating point) 0.5. Questo oggetto viene immagazzinato da qualche parte in memoria. Poi Python lega un nome all'oggetto (binds a name to the object). Il nome è x
, e spesso ci si riferisce a x
come a una variabile, un oggetto, o persino il valore 0.5. Tuttavia, tecnicamente, x
è il nome che è legato all'oggetto 0.5
. Un altro modo per dirlo è che x
è una referenza (reference) all'oggetto.
Mentre in genere è sufficiente pensare di assegnare 0.5 alla variabile x, ci sono situazioni in cui è necessario ricordarsi di quello che effettivamente succede. In particolare, quando passiamo, come argomento a delle funzioni, delle reference a oggetti , dobbiamo essere coscienti che le funzioni possono agire sull'oggetto piuttosto che su una copia dell'oggetto. Tutto questo verrà esaminato con maggiori dettagli in 06_funzioni_moduli.
In un programma troviamo spesso espressioni come
x = x + 1
Se interpretiamo questa espressione come una equazione, come siamo abituati a fare in matematica, in $x = x + 1$ potremmo sottrarre $x$ da entrambi i membri, ottenendo 0 = 1. Sappiamo che questo non è vero quindi da qualche parte c'è qualcosa che non torna.
La risposta è che le “equazioni“ nei programmi non sono equazioni ma assegnazioni. Devono sempre essere comprese come una sequenza di due passi:
Calcola il valore dell'espressione a destra dell'uguale
Assegna questo valore alla variabile il cui nome appare sulla sinistra. (In Python: lega il nome sulla sinistra all'oggetto che si trova a destra.)
Qualche testo di Informatica usa la notazione seguente per esprimere l'assegnazione e evitare confusioni con le equazioni matematiche:
$$x \leftarrow x + 1$$Applichiamo la procedura a due passi all'espressione x = x + 1
vista sopra:
Calcola il valore dell'espressione a destra dell'uguale: per farlo dobbiamo conoscere il valore attuale di x
. Assumiamo che x
attualmente sia 4
. In questo caso, il membro di destra, x+1
, viene valutato come 5
.
Assegna questo valore (i.e. 5
) alla variabile il cui nome appare sulla sinistra, x
.
Controlliamo che questa interpretazione sia corretta:
x = 4
x = x + 1
x
+=
¶È molto comune dover aumentare la variabile x
di una quantità costante c
. Possiamo scrivere:
x += c
invece di
x = x + c
Il nostro esempio iniziale avrebbe potuto essere scritto come
x = 4
x += 1
x
Lo stesso tipo di operatore è definito per la moltiplicazione per una costante (*=
), suttrazione di una costante (-=
) e divisione per una costante (/=
).
Notate che l'ordine fra +
e =
è rilevante:
x += 1
incrementa la variabile x
di uno mentre
x =+ 1
assegna il valore +1
alla variabile x
.
Le funzioni ci permettono di raggruppare una sequenza di comandi in un blocco logico. Comunichiamo con una funzione attraverso una interfaccia ben definita, fornendo dei parametri, e ricevendo di ritorno delle informazioni. Generalmente non sappiamo esattamente come una funzione ottiene il valore che ci restituisce, conosciamo solo l'interfaccia.
Per esempio la funzione math.sqrt
: non sappiamo esattamente come calcola la radice quadrata, ma conosciamo l'interfaccia: se passiamo il valore x alla funzione, ci restituisce (un valore approssimato per) $\sqrt{x}$.
Questa astrazione è utile: è una tecnica comune dividere un sistema, un problema in componenti più piccole che funzionano insieme attraverso delle interfacce ben definite che non hanno bisogno di conoscere i dettagli di come ciascuna realizza il suo compito. In fatti, non doversi preoccupare dei dettagli di implementazione ci permette di avere una visione più chiara di un sistema composto di molte parti.
Le funzioni costituiscono i mattoncini fondamentali all'interno di programmi più vasti e aiutano a tenere sotto controllo la complessità intrinseca dei problemi.
Nella programmazione, la parola “funzione” si riferisce ad una sequenza, dotata di nome in modo da poter essere "chiamata", di operazioni che svolgono un determinato calcolo. Per esempio, la funzione sqrt()
nel modulo math
calcola la radice quadrata di un dato valore:
import math
math.sqrt(4)
Il valore che passiamo alla funzione sqrt
in questo caso è 4. Questo valore viene chiamato argomento della funzione. Una funzione può avere più argomenti.
La funzione ritorna il valore 2.0 (il risultato del calcolo) all'“ambiente di chiamata”. Questo valore si chiama il return value della funzione.
Si dice in genere che la funzione prende un argomento e ritorna un risultato o return value.
Il format generico della definizione di una funzione è:
def my_function(arg1, arg2, ..., argn): # Notate i ":" finali
"""Optional docstring."""
# Implementation of the function
comando1 # Notate l'indentazione di 4 spazi di tutti i comandi contenuti all'interno della funzione
comando2
return result1, result2 ... # optional
#this is not part of the function
some_command # Notate l'assenza di indentazione
La funzione inizia con la keyword def
, seguita dal nome che assegnamo alla funzione, dalla lista, tra parentesi, degli argomenti che devono essere passati alla funzione quando questa viene eseguita e dal simbolo :
che chiude la dichiarazione della funzione.
Tutto il corpo della funzione, l'insieme delle operazioni che devono essere eseguite sugli argomenti in input, devo essere indentati di quattro spazi rispetto alla keyword def
.
È buona pratica inserire una docstring
, fra tripli doppi apici(""") che spieghi cosa fa la funzione. La docstring
può essere mostrata con
help(nome_funzione)
.
In genere la funzione restituisce i risultati delle proprie operazioni al programma esterno attraverso la keyword return
La definizione della funzione si conclude quando il codice smette di essere indentato.
Una funzione, una volta definita e messa in memoria eseguendo la cella, può essere chiamata più volte, anche con argomenti diversi.
Stampare un valore all'interno di una funzione non significa rendere disponibile il valore all'esterno della funzione. In particolare
return print(a)
stampa il valore della variabile a
sullo schermo ma restituisce None
(v. Sez "Un errore comune").
Le parentesi dopo il nome della funzione sono necessarie. Se una funzione non ha argomenti si scrive:
def my_function2():
Se si omettono le parentesi si ha un errore:
def pippo:
pass
La terminologia di Allen Downey (nel suo libro Think Python) di funzioni fruttuose e infruttuose distingue fra funzioni che ritornano un valore (=fruitful) e quelle che non lo fanno (=fruitless). Se una funzione non usa il comando return
, diciamo che non ritorna nulla (mentre, in realtà ritorna sempre l'oggetto None
– anche se il comando return
manca).
Per esempio, la funzione greeting
, quando viene chiamata, stampa “Hello World” (ed è fruitless perché non ritorna alcun valore).
def greeting():
print("Hello World!")
Se la chiamiamo:
greeting()
stampa “Hello World” su stdout (lo standard output, in questo caso la nostra finestra), come ci si aspetta. Se assegnamo il return value della funzione a una variabile x
, la possiamo successivamente esaminare/utilizzare:
x = greeting()
print(x)
confermandondo che la funzione greeting
ha effettivamente ritornato l'oggetto None
.
In genere, funzioni che ritornano uno o più valori sono più utili perché possono essere combinate per costruire il codice (magari all'interno di un'altra funzione).
Una funzione che prende come input due numeri e ne restituisce la somma.
def my_sum(a,b):
"""
my_sum calcola la somma di due valori.
a, b : input
c : output
"""
c = a+b
return c
my_sum(4,5)
Per poter utilizzare il risultato di una funzione all'esterno della funzione stessa è necessario assegnarlo ad una variabile:
f1 = my_sum(3.7,-2.4)
f2 = f1*f1
print(f2)
La docstring fornisce informazioni sullo scopo della funzione. Si può richiamare con help
help(my_sum)
Una funzione senza input che restituisce il valore di $\pi/2$:
def my_half_pi():
return 3.141592653589793/2.
my_half_pi()
Una funzione che restituisce due valori:
def my_sum_dif(a,b):
sum = a + b
dif = a - b
return sum, dif
I due valori restituiti da my_sum_dif
possono essere assegnati a due variabili separando i nomi delle variabili con una virgola.
p = 8
q = 2
r1,r2 = my_sum_dif(p,q)
print(p,'+',q,'=',r1)
print(p,'-',q,'=',r2)
Una funzione che restituisce le soluzioni di una equazione algebrica di secondo grado, cioè i valori di x
tali che a*x**2 + b*x +c = 0
. Gli input necessari sono a, b, c
.
import math
def my_sol_eq2(a,b,c):
disc = math.sqrt(b*b-4.*a*c)
sol1 = (-b+disc)/2./a
sol2 = (-b-disc)/2./a
return sol1,sol2
a = 1
b = 2
c =1
r1,r2 = my_sol_eq2(a,b,c)
print('sol+ :',r1)
print('sol2 :',r2)
Possiamo definire più funzioni di Python in un singolo file e chiamare una funzione in una funzione diversa. Ecco un esempio:
def returnstars( n ):
return n * '*'
def print_centered_in_stars( string ):
linelength = 46
starstring = returnstars((linelength - len(string)) // 2)
print(starstring + string + starstring)
print_centered_in_stars('Hello world!')
È un errore comune, quando si inizia, confondere stampare un valore con ritornare un valore. Nell'esempio seguente è difficile capire se la funzione math.sin
ritorna un valore oppure lo stampa:
import math
math.sin(2)
Importiamo il modulo math
, e chiamiamo la funzione math.sin
con argomento 2
. La chiamata math.sin(2)
, in effetti, ritorna il valore 0.909...
, non lo stampa. Tuttavia, siccome non abbiamo assegnato il valore a una variabile, la sessione interattiva di Python stampa sullo schermo il valore ritornato.
La sequenza alternativa che segue funziona solamente se viene restituito un valore:
x = math.sin(2)
print(x)
Il risultato della chiamata math.sin(2)
viene assegnato alla variabile x
, e , alla linea successiva, x
viene stampato.
In genere le funzioni vengono eseguite in modo "silenzioso" (cioè non stampano nulla) e comunicano il risultato del loro conto attraverso il return value.
Parte della confusione fra valori stampati e valori ritornati in una sessione interattiva deriva dal fatto che Python stampa il valore che viene ritornato se il valore non viene assegnato a una variabile. In genere, noi vogliamo vedere quello che viene ritornato, per poterci orientare. Bisogna però superare questa confusione iniziale.
def square1(a):
sq = a*a
return print(sq)
Testiamo la funzione:
square1(3)
Apparentemente square
funziona, ma, in realtà, stampa solo il risultato sullo schermo, NON restituisce il valore.
Per verificarlo, assegniamo quanto restituito dalla funzione ad una variabile o poi stampiamone il contenuto:
d = square1(5)
print('Il valore della variabile d è:',d)
Il comando return print(sq)
STAMPA sullo schermo il risultato ma RESTITUISCE None
.
Il modo corretto di fare è:
def square2(a):
sq = a*a
return sq
square2(3) #
d = square2(5)
print('Il valore della variabile d è:',d)
Se non c'è il return
la funzione restituisce None
:
def square3(a):
sq = a*a
d=square3(3)
print('La funzione square3 restituisce:',d)