Fondamenti di Programmazione

Primi Passi in Python

Grazie al fatto che Python è un linguaggio interpretato può essere usato interattivamente tramite una shell. Ed è da qui che iniziamo usando o la shell della linea di comando, invocando l'interprete python, o quella fornita da IDLE. Quando si invoca l'interprete, >>> viene stampato prima dei comandi e i risultati seguono.

Espressioni Aritmetiche

Python supporta le comuni operazioni aritmetiche su numeri: addizione +, sottrazione -, moltiplicazione *, divisione /.

>>> 15 + 3
18
>>> 10 - 25
-15
>>> 3*7
21
>>> 18/5
3

Possono essere usate sia con numeri interi che con numeri con virgola:

>>> 15.7 + 3
18.7
>>> 18.0/5
3.6

Se almeno uno degli operandi è un numero con virgola, l'operazione è automaticamente effettuata in virgola mobile. In particolare, se gli operandi della divisione / sono entrambi interi (cioè non hanno la virgola, che in realtà è un punto floating point) il risultato è troncato dei decimali ed è sempre un intero, altrimenti il risultato non è troncato ed è sempre un numero con virgola. In Python i numeri interi sono di tipo int e quelli con virgola sono di tipo float. Parleremo dei tipi più avanti.

Per raggruppare risultati parziali si possono usare le parentesi tonde:

>>> (12/5)*(16 - 2*3)
20

Gli operatori * e / hanno la precedenza su + e -, così in 16 - 2*3 prima è effettuata la moltiplicazione e poi la sottrazione.

L'operatore resto o modulo è %:

>>> 24 % 7
3
>>> 27 % 9
0
>>> 27 % 7.5
1.5

L'operatore di esponenziazione e **:

>>> 2**8
256
>>> 2**0.5
1.4142135623730951

I risultati delle operazioni con interi sono a precisione illimitata mentre quelle con float hanno una precisione limitata:

>>> 2**200
1606938044258990275541962092341162602522202993782792835301376L
>>> 2.0**200
1.6069380442589903e+60

Commenti

In python si possono inserire commenti all'interno dei programmi. Questi servono per lasciare note sul funzionamento del codice, sia per se stessi, sia per altri. In python, ogni riga che inizia col symbolo # è un commento e non viene eseguito dall'interprete. La sintassi è quindi

# commento

Variabili e Assegnamenti

Per facilitare la memorizzazione di valori e il loro successivo recupero ed utilizzo durante l'esecuzione di un programma, tutti i linguaggi di programmazione permettono di usare le variabili. Una variabile è un nome a cui possiamo associare un valore. Il nome di una variabile può comprendere lettere, cifre e l'underscore _, ma non deve iniziare con una cifra. Una variabile è creata nel momento in cui gli è assegnato un valore:

>>> pigreco = 3.14

Questo avviene tramite un assegnamento che è un costrutto del tipo nome_variabile = espressione. Il significato è: valuta l'espressione a destra del segno = e poi assegna il valore del risultato alla variabile il cui nome è a sinistra del segno =.

>>> # area di un cerchio di raggio 10
>>> raggio = 10
>>> area = pigreco*(raggio**2)
>>> area
314.0

Le variabili sono chiamate così perché il valore assegnato può essere cambiato. Ad esempio, possiamo ricalcolare l'area resettando il raggio.

>>> recalcolo dell'area con raggio 20
>>> raggio = 20
>>> area = pigreco*(raggio**2)

Ecco una visualizzazione del cambiamento di valori nelle variabili in un piccolo programma.

pigreco = 3.14159
raggio = 10
area = pigreco*(raggio**2)
raggio = 20
area = pigreco*(raggio**2)

All'inizio alcuni assegnamenti potranno sembrare un po' strani a causa del segno = che in matematica significa uguaglianza:

>>> # area di un cilindro con raggio 10 e altezza 5
>>> altezza = 5
>>> circonferenza = 2*pigreco*raggio
>>> area = 2*area + altezza*circonferenza
>>> area
942.477

Ma basta riflettere sul significato che ha il segno = in un assegnamento per fugare qualsiasi perplessità.

Errori

Quando si programma è abbastanza facile commettere errori. Per adesso gli errori saranno molto semplici e quindi facilmente correggibili. Ma più ci addentreremo nella programmazione e più gli errori diventeranno complicati, subdoli e quindi difficili da individuare. Ogni programmatore sa che deve convivere con gli errori e deve imparare a fronteggiarli. Questa è una abilità che si impara gradualmente con l'esperienza. Gli errori più facili sono quelli relativi alla sintassi del linguaggio di programmazione. Tutti i linguaggi mostrano opportuni messaggi di errore quando si viola la loro sintassi. Così fa Python:

>>> raggio = 2*ragio
Traceback (most recent call last):
  File "<pyshell#92>", line 1, in <module>
    raggio = 2*ragio
NameError: name 'ragio' is not defined

L'ultima linea del messaggio è per ora quella che ci interessa NameError: name 'ragio' is not defined. Questa ci avverte che la variabile di nome ragio non esiste e quindi l'istruzione non può essere eseguita.

Funzioni

Ritornando al piccolo programma che abbiamo scritto per calcolare l'area del cilindro, se volessimo calcolare l'area con un differente raggio o altezza dovremmo riscriverlo daccapo quasi interamente. Ciò è tedioso anche per quelle poche istruzioni figuriamoci se fossero molte di più. Per fortuna, così come le variabili aiutano a usare i valori dandogli nomi, un meccanismo simile permette di dare un nome a un programma.

>>> def area_cilindro(raggio, altezza):
        pigreco = 3.14159
        area = pigreco*(raggio**2)
        circonferenza = 2*pigreco*raggio
        return 2*area + altezza*circonferenza

La parola chiave def introduce la definizione di una funzione. Una funzione è un programma a cui è stato dato un nome in modo che possa essere chiamato, cioè eseguito, tramite quel nome.

>>> area_cilindro(10, 5)
942.477
>>> area_cilindro(20, 10)
3769.908

La forma generale della definizione di una funzione è

def nome_funzione(parametri):
    istruzioni

La definizione crea un nome nome_funzione, nel nostro caso specifico area_cilindro, a cui è assegnato un valore un po' più complicato di un numero, un programma. Dopo il nome, tra parentesi, ci sono zero, uno o più parametri. Un parametro è una variabile a cui è assegnato un valore quando la funzione è chiamata. Ad esempio, la chiamata area_cilindro(10, 5) assegna il valore 10 al parametro raggio e 5 a altezza immediatamente prima di eseguire le istruzioni della funzione. Quindi i parametri permettono di fornire gli input al programma definito nella funzione. Dopo i parametri ci sono i : e poi a partire dalla prossima linea, indentate di 4 spazi ci sono le istruzioni che saranno eseguite ad ogni chiamata della funzione. L'output della funzione è prodotto tramite il costrutto return la cui forma generale è

return espressione

e quando è eseguito valuta l'espressione e poi termina l'esecuzione della funzione ritornando il valore dell'espressione.

Tutte le variabili create in una funzione, compresi i parametri, sono locali alla funzione, cioè non esistono al di fuori della funzione. Nella funzione area_cilindro abbiamo usato variabili con lo stesso nome di variabili create al di fuori della funzione però non sono le stesse variabili. Le variabili create al di fuori di ogni funzione sono dette variabili globali. Ad esempio, i valori delle variabili globali area e raggio sono

>>> area
942.477
>>> raggio
10

Se fossero le stesse variabili omonime della funzione, area dovrebbe avere il valore 1256.636 e raggio il valore 20. Vedremo più avanti in che modo le variabili globali possono essere usate in una funzione.

Ecco una visualizzazione del comportamento della funzione:

def area_cilindro(raggio, altezza):
    pigreco = 3.14159
    area = pigreco*(raggio**2)
    circonferenza = 2*pigreco*raggio
    return 2*area + altezza*circonferenza

area1 = area_cilindro(10, 5)
area2 = area_cilindro(20, 10)

print area1, area2

Per stampare il risultato abbiamo usato il costrutto print che permette di stampare qualsiasi espressione:

print espressione

Per far capire meglio che in Python il nome di una funzione non è differente dal nome di una variabile, possiamo assegnare a area_cilindro un altro valore:

>>> area_cilindro = 13
>>> area_cilindro
13

Ora il valore assegnato alla variabile area_cilindro non è più la funzione che abbiamo definito sopra ma l'intero 13. Infatti,

>>> area_cilindro(10, 5)
Traceback (most recent call last):
    File "<pyshell#1>", line 1, in <module>
        area_cilindro(10, 5)
TypeError: 'int' object is not callable

Se vogliamo che area_cilindro sia ancora quella funzione dobbiamo ridefinirla.

Durante il corso scriveremo tantissime funzioni, anche perchè è difficile scrivere un programma in Python (o in un altro linguaggio) senza usare funzioni. Per adesso come ulteriore esempio consideriamo una funzione che prende in input un numero di secondi e stampa l'equivalente in ore, minuti e secondi:

>>> def hms(nsec):
        hh = nsec/3600
        nsec = nsec % 3600
        mm = nsec/60
        ss = nsec % 60
        print hh, mm, ss

La funzione hms() è un esempio di funzione che non ritorna un valore ma il cui effetto è di stampare qualcosa. In realtà anche hms() ritorna, implicitamente, un valore vedremo questo in seguito. Chiamando la funzione si ha

>>> hms(4000)
1 6 40
>>> hms(100000)
27 46 40

Se proviamo a vedere il valore della variabile hh otteniamo

>>> hh
Traceback (most recent call last):
   File "<pyshell#122>", line 1, in <module>
       hh
NameError: name 'hh' is not defined

a ulteriore dimostrazione che le variabili locali di una funzione non esistono al di fuori della funzione.

Python ha molte funzioni già pronte e disponibili, dette appunto built-in functions. Ad esempio abs() che ritorna il valore assoluto e round() che arrotonda un valore con virgola al più vicino intero:

>>> abs(-5)
5
>>> round(5.8)
6.0
>>> round(5.3)
5.0

File e Moduli

Finora abbiamo usato la shell per provare piccoli programmi in modo interattivo ma ovviamente un vero programma deve essere usabile e modificabile quando si vuole senza doverlo riscrivere daccapo ogni volta. Basta creare un file di testo con un qualsiasi editor, anche l'editor incluso in IDLE, scriverci il nostro programma e salvarlo con estensione .py. Ad esempio, possiamo creare un file con nome primo.py in cui scriviamo:

def area_cilindro(raggio, altezza):
    pigreco = 3.14159
    area = pigreco*(raggio**2)
    circonferenza = 2*pigreco*raggio
    return 2*area + altezza*circonferenza

print area_cilindro(10, 5), area_cilindro(20, 10)

def hms(nsec):
    hh = nsec/3600
    nsec = nsec % 3600
    mm = nsec/60
    ss = nsec % 60
    print hh, mm, ss

print hms(4000), hms(100000)

Per eseguire il programma basterà andare sul terminale (il prompt dei comandi su Windows), posizionarsi nella directory che contiene il file primo.py e digitare python primo.py. Questo comando invoca l'interprete Python fornendogli in input il nostro file che sarà eseguito.

Un qualsiasi programma in Python è anche un modulo. Un modulo è una collezione di funzioni raggruppate in un singolo file. Di solito, le funzioni in un modulo sono in qualche modo connesse tra loro, ad esempio potrebbero essere funzioni matematiche o funzioni per la manipolazione dei files o per la grafica, ecc. L'aspetto importante è che le funzioni in un modulo possono essere usate in un qualsiasi programma senza doverle riscrivere. Per poter usare le funzioni in un modulo bisogna importarle tramite il costrutto import. Ad esempio, la libreria standard di Python consiste in tantissimi moduli fra cui il modulo math. Se vogliamo usare le funzioni del modulo math basterà scrivere, in un programma o nella shell:

 >>> import math
 >>> math.log(10)
 2.302585092994046
 >>> math.pi
 3.141592653589793
 >>> math.factorial(20)
 2432902008176640000

I nomi delle funzioni vanno precedute dal nome del modulo, altrimenti l'interprete non sa dove andarle a cercare e sopratutto potrebbero essere state definite altrove delle funzioni con lo stesso nome.

Per avere un elenco delle funzioni in un modulo si può usare la funzione dir():

>>> dir(math)
['__doc__', '__file__', '__name__', '__package__', 'acos', 'acosh', 'asin', 'asinh', 'atan', 'atan2','atanh', 'ceil', 'copysign', 'cos', 'cosh', 'degrees', 'e', 'erf', 'erfc', 'exp', 'expm1', 'fabs', 'factorial', 'floor', 'fmod', 'frexp', 'fsum', 'gamma', 'hypot', 'isinf', 'isnan', 'ldexp', 'lgamma', 'log', 'log10', 'log1p', 'modf', 'pi', 'pow', 'radians', 'sin', 'sinh', 'sqrt', 'tan', 'tanh', 'trunc']

Inoltre, possiamo avere la documentazione di ogni funzione del modulo (dopo averlo importato) con la funzione help():

>>> help(math.log)
Help on built-in function log in module math:

log(...)
    log(x[, base])

    Return the logarithm of x to the given base.
    If the base not specified, returns the natural logarithm (base e) of x.

La funzione help() può anche essere usata fornendogli in input il nome di un modulo e in questo caso mostra la documentazione dell'intero modulo, cioè di tutte le funzioni contenute in esso.

Quando si fa l'import di un modulo l'interprete deve sapere dove cercare il corrispondente file. Nel caso dei moduli della libreria standard come math non ci sono problemi perché si trovano in una directory preimpostata. Ma nel caso di un modulo che abbiamo scritto noi o qualcun'altro la situazione è differente. L'interprete lo cerca in alcune directories preimpostate tra cui c'è anche la directory del file del modulo che lo importa. Per cui un modo molto semplice che assicura l'import è di metterlo nella stessa directory. Python permette la massima libertà nell'import di moduli, ma lo vedremo più avanti.

Esercizi

  1. Scrivere una funzione scontato che prende in input un importo e una percentuale di sconto e ritorna l'importo scontato. Ad esempio, se l'importo è 1000 e lo sconto è 20, la funzione ritorna 800.

  2. Scrivere una funzione secondi che prende in input un lasso di tempo espresso tramite numero di ore hh, numero di minuti mm e numero secondi ss e ritorna l'equivalente in numero secondi. Ad esempio, hh = 2, mm = 1 e ss = 11, la funzione ritorna 7271.

  3. Scrivere una funzione invest che prende in input un capitale C, un interesse annuale i e un numero di anni n e ritorna il capitale maturato dopo un investimento di n anni all'interesse i. Ad esempio, se C = 1000, i = 10 e n = 2, la funzione ritorna 1210.