10: USARE I MODULI

LE FUNZIONI

Abbiamo già anticipato nella lezione 4 che in Python possiamo usare migliaia di funzioni. Esse non sono però tutte disponibili immediatamente, per un motivo soprattutto logistico: l'interprete Python che legge le nostre istruzioni è esso stesso un programma, ed incorporare in esso tutte le funzioni avrebbe portato (per chi lo ha programmato) ad unico file enorme e poco maneggevole (ad esempio l'aggiunta o la correzione di una funzione avrebbe portato alla modifica dell'intero programma che gestisce Python).

Quindi si è scelto di rendere immediatamente disponibili solo poche funzioni (ad esempio max(), min(), len(), int() che abbiamo già visto) mentre le altre sono raggruppate in files esterni, chiamati moduli. Ogni modulo contiene funzioni attinenti ad un argomento (matematica, grafica, tempo, interfaccia con il sistema operativo ...): quando dobbiamo usare queste funzioni possiamo dire a Python di importare quel modulo e così potremo usare le sue funzioni.

Cosa dobbiamo fare quindi per usare una funzione che si trova in un modulo? In questa lezione faremo un pò di esempi lavorando in IDLE. Proviamo ad esempio con la radice quadrata; digitiamo:


>>> sqrt(4)

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

La funzione sqrt() non è una delle funzioni immediatamente disponibili, quindi Python riporta un NameError e ci avvisa che non la conosce.

IMPORTARE I MODULI

Per poter usare la sqrt() dobbiamo per prima cosa sapere in quale modulo è contenuta: questo è il modulo math, che contiene tutte le funzioni matematiche.

Per "far conoscere" a Python le funzioni contenute in un dato modulo si usa il comando import: esso ha molte sintassi possibili, ma noi ne faremo vedere solo tre. Scriviamo:


>>> import math
>>> math.sqrt(4)

2.0

L'istruzione import nome_modulo rende disponibili tutte le funzioni e le costanti definite nel modulo. Possiamo fare riferimento ad esse usando la sintassi:

nome_modulo.nome_funzione()

(cioè il nome del modulo, il punto ed il nome della funzione)

Ad esempio possiamo continuare così:


>>> math.sqrt(4)

2.0

>>> math.log(1)     # logaritmo

0.0

>>> math.cos(0)     # coseno

1.0

>>> math.exp(1)     # esponenziale

2.718281828459045

(per i nostri scopi non è importante che conosciate il logaritmo, il coseno o l'esponenziale: vi basta sapere che sono delle funzioni matematiche).

Ora resettiamo IDLE cliccando la voce di menù Shell=>Restart Shell in modo da cancellare l'effetto del primo import. Scriviamo:


>>> from math import sqrt, log
>>> sqrt(4)

2.0

>>> log(1)

0.0

>>> cos(0)

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

L'istruzione from nome_modulo import nome_funzione rende disponibili solo i nomi indicati in essa (se ne possono scrivere molti, separati dalla virgola). Possiamo fare riferimanto ad essi direttamente (senza anteporre il nome del modulo e il punto).

Infatti l'istruzione che abbiamo scritto ha importato la radice sqrt() ed il logaritmo log(), ma non il coseno che ha dato un errore. Notate che in questo esempio è sparito il math. prima del nome delle funzioni.

Di nuovo clicchiamo Shell=>Restart Shell e continuiamo così:


>>> from math import *
>>> sqrt(4)

2.0

>>> exp(1)

2.718281828459045

L'istruzione from math import * rende disponibili tutte le funzioni e le costanti definite nel modulo. Possiamo riferirci ad esse direttamente (senza anteporre il nome del modulo).

Questa terza sintassi è sicuramente più comoda e probabilmente vi starete chiedendo che bisogno ci sia delle altre due, che richiedono di digitare più tasti per chiamare le funzioni. Il fatto è che importando direttamente tutto il modulo non sappiamo esattamente cosa abbiamo importato. Un grosso programma può importare molti moduli, ed in alcuni di essi potrebbero esserci funzioni con lo stesso nome: quindi chiamando la funzione xxx() (senza il nome del modulo) non sappiamo esattamente quale funzione stiamo chiamando. Ecco perchè i programmatori esperti preferiscono "avere il controllo" sui nomi delle funzioni, usando le prime due sintassi.

Nel seguito, comunque, dato che i nostri programmi importeranno al massimo uno o due moduli, useremo sempre la terza forma che permette una sintassi più semplice. E' uso comune mettere le istruzioni import all'inizio del programma, per essere sicuri che precedano tutte le funzioni che poi useremo.

Quindi, se in uno dei prossimi esercizi vi dirò di usare la funzione xxx() che si trova nel modulo yyy dovrete scrivere questo codice:


from yyy import *
...
...
a = xxx()

ALCUNE FUNZIONI DI AIUTO

La distribuzione standard di Python comprende decine di moduli; la lista dei moduli si può facilmente trovare nella documentazione ufficiale e varia da versione a versione di Python (le funzioni immediatamente disponibili appartengono al modulo __builtins__ che viene automaticamente caricato all'avvio). Inoltre da Internet si possono scaricare ed installare altri moduli di programmatori indipendenti che riguardano, ad esempio la grafica, i giochi, la musica.

Per sapere "cosa contiene" un modulo ci sono alcune funzioni di aiuto. Dopo aver importato un modulo in IDLE, possiamo scrivere dir(nome_modulo) per ottenere la lista di tutti i nomi definiti nel modulo (oltre alle funzioni ci possono essere anche delle costanti: ad esempio in math sono definiti pi greco ed il numero di Eulero); scrivendo help(nome_modulo) si ottiene una spiegazione più approfondita (in inglese). Invece help(nome_funzione) ci dà una breve spiegazione (sempre in inglese) su quello che fa la funzione.

ESERCIZIO 10.1: Il modulo time
Questo modulo contiene alcune funzioni legate al tempo. Apriamo IDLE ed importiamolo. Possiamo poi usare dir() e help() per cercare di capire quali funzioni contiene. Vediamone qualcuna
>>> asctime()
Restituisce una stringa leggibile con la data di oggi.
>>> sleep(n)
Sospende l'esecuzione per n secondi (n può essere un float per avere una precisione minore del secondo). Attenti a non usare un valore grande perchè sembrerà che il programma si pianti (in realtà sta aspettando...).
>>> time()
Restituisce il numero di secondi passati da una data predefinita ("The epoch"). E' utile per calcolare intervalli di tempo. Ad esempio provate questa:

>>> t1 = time()
>>> t2 = time()
>>> t2 - t1
t2 - t1 ci da numero di secondi trascorsi tra le due chiamate di time()

IL MODULO random

Contiene una serie di funzioni legate alla generazione di numeri casuali ed è molto usato per i giochi, perchè può simulare il lancio di una moneta o di un dado. Ecco alcune delle funzioni contenute:

Funzione Significato
seed() Serve per inizializzare il generatore di numeri casuali. Va sempre chiamata (una sola volta) prima di usare qualsiasi altra funzione del modulo
randrange(n) Restituisce un numero intero casuale tra 0 ed n (non compreso). Quindi con randrange(2) si può simulare il lancio di una moneta (restituirà casualmente 0 o 1)
randrange(m, n) Restituisce un numero intero casuale tra m (compreso) ed n (non compreso). Quindi con randrange(1, 7) si può simulare il lancio di un dado. Abbiamo già usato questa funzione qui.
random() Restituisce un float casuale compreso tra 0.0 e 1.0. Si usa spesso per eseguire del codice in modo probabilistico. Ad esempio, scrivendo
if random() < 0.2:
     . . .
il blocco dell'if verrà eseguito il 20% delle volte

Ad esempio proviamo a simulare il lancio di un dado con IDLE:


>>> from random import *
>>> seed()
>>> randrange(1, 7)

5

>>> randrange(1, 7)

3

>>> randrange(1, 7)

1

possiamo continuare a chiamare la stessa randrange() quante volte vogliamo, ed ogni volta ci restituirà un numero a caso tra 1 e 6. Usiamo adesso le funzioni di random per programmare un altro gioco.

NUOVO PROGRAMMA: gioco_banco.py
ESERCIZIO 10.2 PROGRAMMA PASSO PASSO: gioco_banco.py
Scrivere il programma a partire da questo schema:
  1. importare il modulo random e chiamare seed()
  2. definire una variabile punti = 100
  3. chiedere all'utente quanto vuole puntare e assegnare questo valore ad una variabile puntata (sarà un numero intero)
  4. estrarre una carta, cioè un numero da 1 a 10: dovremo assegnare ad una variabile carta il valore di una randrange(): ATTENZIONE! per avere un risultato tra 1 e 10 i due parametri dovranno essere ...
  5. stampare "E' uscito ..."
  6. se carta è minore di 6 hai perso la somma puntata: aggiorna punti
  7. altrimenti hai vinto: aggiorna punti
  8. stampa un messaggio con il nuovo punteggio
Esempio di I/O
Quanto vuoi puntare? 10
E' uscito 4
Hai perso! Il tuo nuovo punteggio e' 90
ESERCIZIO 10.3: Più estrazioni
Il programma precedente ti permette di giocare una sola volta. Per giocare più volte devi
  1. Dopo aver inizializzato la variabile punti, inserire tutti i passi successivi all'interno di un ciclo while, che deve terminare o quando punti arriva a 0 (hai perso tutto) o quando punti vale 200 o più (hai raddoppiato il punteggio iniziale). ATTENZIONE! Queste sono le condizioni per uscire, ma tu devi inserire quelle per ripetere il ciclo ...
  2. All'uscita del ciclo, stampare una frase di scherno (se l'utente ha perso) o di ammirazione (se ha vinto)
ESERCIZIO 10.4: Più controllo
Il programma precedente non controlla se stai puntando più del tuo punteggio. Dovresti inserire la puntata in un ciclo while che non permetta all'utente di farlo. (SUGGERIMENTO: hai già programmato questo codice nell'ESERCIZIO 9.2. Riutilizzalo incollandolo nel programma e facendo i necessari adattamenti).
SOLUZIONI

Fine della lezione