Python: Funzioni¶
Le funzioni sono blocchi di codice a cui associamo un nome.
Per definire una nuova funzione, scrivo:
def nome_funzione(argomento_1, argomento_2, ...):
# qui metto il codice che usa argomento_1, argomento_2,
# etc. per calcolare il valore della variabile risultato
return risultato
Una volta definita la funzione, la posso chiamare/invocare dal resto del codice, cosi’:
valore_ritornato = nome_funzione(valore_1, valore_2, ...)
# qui uso valore_ritornato
Esempio. Definisco una funzione che prende due argomenti (che chiamo
arbitrariamente numero1
e numero2
) e ne stampa la somma:
def stampa_somma(numero1, numero2):
print("la somma e'", numero1 + numero2)
che uso cosi’:
# stampa: la somma e' 10
stampa_somma(4, 6)
# stampa: la somma e' 17
stampa_somma(5, 12)
# stampa: la somma e' 20
stampa_somma(19, 1)
Il codice qui sopra e’ del tutto equivalente a questo:
numero1 = 4
numero2 = 6
print("la somma e'", numero1 + numero2)
numero1 = 5
numero2 = 12
print("la somma e'", numero1 + numero2)
numero1 = 19
numero2 = 1
print("la somma e'", numero1 + numero2)
Warning
In stampa_somma()
non c’e’ un return
. Quando manca il return
,
il risultato della funzione e’ sempre None
:
risultato = stampa_somma(19, 1) # stampa: la somma e' 20
print(risultato) # stampa: None
Esempio. Riscrivo la funzione stampa_somma()
, che stampava la somma
dei suoi argomenti, in modo che invece restituisca (con return
) la
somma come risultato:
def calcola_somma(numero1, numero2):
return numero1 + numero2
Quando la chiamo succede questo:
# non stampa niente
calcola_somma(4, 6)
# non stampa niente
calcola_somma(5, 12)
# non stampa niente
calcola_somma(19, 1)
Perche’? Proviamo a riscrivere quest’ultimo codice per esteso:
numero1 = 4
numero2 = 6
numero1 + numero2
numero1 = 5
numero2 = 12
numero1 + numero2
numero1 = 19
numero2 = 1
numero1 + numero2
Qui e’ vero che effettivamente calcolo le varie somme, ma e’ anche vero
che non le stampo mai: non c’e’ nessun print
!
Quello che devo fare e’ mettere il risultato di calcola_somma()
in una
variabile, e poi stamparlo a parte, cosi’:
# non stampa niente
risultato = calcola_somma(19, 1)
# stampa 20
print(risultato)
(Oppure, abbreviando: print(calcola_somma(19, 1))
). Scritto per esteso,
questo codice e’ equivalente a:
numero1 = 19
numero2 = 1
risultato = numero1 + numero2
print(risultato)
Ora tutto torna: faccio la somma e ne stampo il risultato.
Warning
Il codice “contenuto” in una funzione non fa niente finche’ la funzione non viene chiamata!
Se scrivo un modulo esempio.py
con questo codice:
def funzione():
print("sto eseguendo la funzione!")
e lo eseguo:
python3 esempio.py
Python non stampera’ niente, perche’ la funzione non viene mai chiamata.
Se voglio che venga eseguita, devo modificare il modulo esempio.py
cosi’:
def funzione():
print("sto eseguendo la funzione!")
funzione() # <-- qui chiamo la funzione
Quano lo eseguo:
python3 esempio.py
Python trova la chiamata alla funzione (l’ultima riga del modulo) ed esegue la funzione: di conseguenza, stampera’:
sto eseguendo la funzione!
Esempio. Creo una funzione fattoriale()
che prende un intero n
e ne calcola il fattoriale n!, definito cosi’:
n! = 1 \times 2 \times 3 \times \ldots (n - 2) \times (n - 1) \times n
So farlo senza una funzione? Certo. Assumiamo di avere gia’ la variabile n
.
Scrivo:
fattoriale = 1
for k in range(1, n + 1):
fattoriale = fattoriale * k
Bene. Come faccio a convertire questo codice in una funzione? Semplice:
def calcola_fattoriale(n):
# n contiene il valore di cui voglio calcolare il fattoriale
# qui inserisco il codice sopra
fattoriale = 1
for k in range(1, n+1):
fattoriale = fattoriale * k
# a questo punto ho calcolato il fattoriale, e lo
# posso restituire
return fattoriale
Ora sono libero di chiamare la funzione quante volte mi pare:
print(calcola_fattoriale(1)) # 1
print(calcola_fattoriale(2)) # 2
print(calcola_fattoriale(3)) # 6
print(calcola_fattoriale(4)) # 24
print(calcola_fattoriale(5)) # 120
o anche in modi piu’ complessi:
lista = [calcola_fattoriale(n) for n in range(10)]
Warning
- Il nome della funzione ed il nome degli argomenti li scegliamo noi!
Quiz. Che differenza c’e’ tra questo frammento di codice:
def calcola(somma_o_prodotto, a, b):
if somma_o_prodotto == "somma":
return a + b
elif somma_o_prodotto == "prodotto":
return a * b
else:
return 0
print(calcola("somma", 10, 10))
print(calcola("prodotto", 2, 2))
e questo?:
def f(operation, x, y):
if operation == "sum":
return x + y
elif operation == "product":
return x * y
else:
return 0
print(f("sum", 10, 10))
print(f("product", 2, 2))
Warning
- Il codice della funzione non vede le variabili esterne alla funzione: vede solo gli argomenti!
- Il codice della funzione puo’ restituire un risultato solo attraverso
return
!
Quiz. Consideriamo questo codice:
def una_funzione(a, b):
somma = a + b
return somma
a = 1
b = 2
somma = 3
una_funzione(100, 100)
print(somma)
Cosa viene stampato a schermo?
Esempio. Definisco due funzioni:
def leggi_fasta(percorso):
righe = open(percorso).readlines()
dizionario = {}
for riga in righe:
if riga[0] == ">":
intestazione = riga
else:
sequenza = riga
dizionario[intestazione] = sequenza
return dizionario
def calcola_istogramma(sequenza):
istogramma = {}
for carattere in sequenza:
if not carattere in istogramma:
istogramma[carattere] = 1
else:
istogramma[carattere] += 1
return istogramma
Date le due funzioni, posso implementare un programma complesso che (1) legge un file fasta in un dizionario, (2) per ciascuna sequenza nel file fasta calcola l’istogramma dei nucleotidi, e (3) stampa ciascun istogramma a schermo:
dizionario_fasta = leggi_fasta(percorso)
for intestazione, sequenza in dizionario_fasta.items():
istogramma = calcola_istogramma(sequenza)
print(istogramma)
Esercizi¶
Data la funzione:
def funzione(arg): return arg
di che tipo e’ il risultato delle seguenti chiamate?
funzione(1)
funzione({"1A3A": 123, "2B1F": 66})
funzione([2*x for x in range(10)])
funzione(2**-2)
Data la funzione:
def addizione(a, b): return a + b
di che tipo e’ il risultato delle seguenti chiamate?
addizione(2, 2)
addizione([1,2,3], [True, False])
addizione("sono una", "stringa")
Creare una funzione
stampa_pari_dispari()
che prende un intero e stampa a schermo"pari"
se il numero e’ pari e"dispari"
altrimenti.Cosa succede se scrivo:
risultato = stampa_pari_dispari(99) print(risultato)
Creare una funzione
calcola_pari_dispari()
che prende un intero e restituisce la stringa"pari"
se il numero e’ pari e la stringa"dispari"
altrimenti.Cosa succede se scrivo:
calcola_pari_dispari(99)
Creare una funzione
controlla_alfanumerico()
che prende una stringa e restituisceTrue
se la stringa e’ alfanumerica (contiene solo caratteri alfabetici o numerici) eFalse
altrimenti.Per controllare se un carattere e’ alfanumerico, usare
in
e la stringa::"ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
.Hint. Anche i caratteri minuscoli possono essere alfanumerici!
Creare una funzione
domanda()
che non prende nessun argomento, chiede all’utente un percorso ad un file e stampa a schermo i contenuti del file. (Testarla ad esempio con il filedata/aatable
.)Creare una funzione
wc()
che prende una stringa e restituisce una tripla (tuple
) di tre valori:- Il primo elemento della coppia deve essere la lunghezza della stringa.
- Il secondo elemento deve essere il numero di a capo nella stringa.
- Il terzo elemento deve essere il numero di parole (separate da spazi o a capo) nella stringa.
Creare una funzione
stampa_dizionario()
che prende un dizionario e stampa a schermo le coppie chiave-valore del dizionario formattate come da esempio sotto.Esempio: quando applico
stampa_dizionario()
a:dizionario = { "arginina": 0.7, "lisina": 0.1, "cisteina": 0.1, "istidina": 0.1, }
che e’ un istogramma di frequenze, il risultato deve essere:
>>> stampa_dizionario(dizionario) istidina -> 10.0% cisteina -> 10.0% arginina -> 70.0% lisina -> 10.0%
Qui l’ordine in cui vengono stampate le righe non importa.
Come sopra, ma le chiavi devono essere ordinate alfanumericamente:
>>> stampa_dizionario_ordinato(dizionario) arginina -> 70% cisteina -> 10% istidina -> 10% lisina -> 10%
Hint: conviene estrarre le chiavi del dizionario, ordinarle a parte, scorrere le chiavi ordinate e di volta in volta stampare la riga corrispondente.
Creare una funzione
crea_lista_di_fattoriali()
che prende un interon
, e restituisce una lista din
elementi.L’
i
-esimo elemento deve essere il fattoriale dii
.Ad esempio:
>>> lista = crea_lista_di_fattoriali(5) >>> print(len(lista)) 5 # 5 elementi, come richiesto >>> print(lista[0]) 1 # e' il fattoriale di 0 >>> print(lista[1]) 1 # e' il fattoriale di 1 >>> print(lista[2]) 2 # e' il fattoriale di 2 >>> print(lista[3]) 6 # e' il fattoriale di 3 >>> print(lista[4]) 24 # e' il fattoriale di 4
Hint: conviene usare la funzione
fattoriale()
definita in uno degli esempi precedenti per calcolare i valori della lista.Creare una funzione
conta_carattere()
che prende due stringhe, la prima che rappresenta testo e la seconda che rappresenta un carattere.La funzione deve restituire il numero di ripetizioni del carattere nel testo.
Ad esempio:
>>> print(conta_carattere("abbaa", "a")) 3 # "a" compare 3 volte >>> print(conta_carattere("abbaa", "b")) 2 # "b" compare 2 volte >>> print(conta_carattere("abbaa", "?")) 0 # "?" non compare mai
Creare una funzione
conta_caratteri()
che prende due stringhe, la prima che rappresenta testo e la seconda che rappresenta un tot di caratteri.La funzione deve restituire un dizionario, in cui le chiavi sono i caratteri da cercare, e il valore associato il loro numero di ripetizioni.
Ad esempio:
>>> print(conta_caratteri("abbaa", "ab?")) {"a": 3, "b": 2, "?": 0}
Creare una funzione
distanza()
che prende due coppie(x1,y1)
e(x2,y2)
di punto bidimensionali e ne restituisce la distanza Euclidea.Hint. La distanza Euclidea e’ \sqrt{(x1-x2)^2 + (y1-y2)^2}
Creare una funzione
sottostringa()
che date due stringhe ritornaTrue
se la seconda e’ sottostringa della prima.Creare una funzione
sottostringhe_non_vuote()
che data una stringa, restituisca la lista delle sue sottostringhe non vuote.Creare una funzione
conta_sottostringhe()
che, date due stringhepagliaio
edago
, ritorni il numero di ripetizioni diago
inpagliaio
.Creare una funzione
sottostringa_piu_lunga()
che date due stringhe restituisca la loro sottostringa comune piu’ lunga.Hint. Si puo’ risolvere usando l’esercizio precedente!
Funzioni (Soluzioni)¶
Soluzione: lo stesso tipo del valore che gli passo! La funzione restituisce il valore dell’argomento senza toccarlo.
- un intero.
- un dizionario.
- una lista.
- un razionale.
Soluzione: la somma o concatenazione dei due argomenti. Quindi:
- un intero.
- una lista.
- una stringa.
Soluzione:
def stampa_pari_dispari(numero): if numero % 2 == 0: print("pari") else: print("dispari") stampa_pari_dispari(98) stampa_pari_dispari(99)
Occhio che
stampa_pari_dispari()
stampa gia’ da se’ a schermo, non e’ necessario fare:print(stampa_pari_dispari(99))
altrimenti Python stampera’:
pari None
Il
None
viene dalprint
aggiuntivo: visto chestampa_pari_dispari()
non hareturn
, il suo risultato e’ sempreNone
. Ilprint
aggiuntivo stampa proprio questoNone
.Soluzione:
def calcola_pari_dispari(numero): if numero % 2 == 0: return "pari" else: return "dispari" print(calcola_pari_dispari(98)) print(calcola_pari_dispari(99))
In questo caso invece, visto che non c’e’ nessun
print
incalcola_pari_dispari()
, e’ necessario aggiungere a mano unprint
che ne stampi il risultato!Soluzione:
def controlla_alfanumerico(stringa): caratteri_alfanumerici = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" alfanumerica = True for carattere in stringa: if not carattere in caratteri_alfanumerici: alfanumerica = False return alfanumerica # testo la funzione print(controlla_alfanumerico("ABC123")) print(controlla_alfanumerico("A!%$*@"))
Posso anche usare
break
per interrompere il ciclofor
appena trovo un carattere alfanumerico (e’ impossibile che una stringa dove ho appena trovato un carattere non-alfanumerico ridiventi alfanumerica), cosi’:def controlla_alfanumerico(stringa): caratteri_alfanumerici = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" alfanumerica = True for carattere in stringa: if not carattere in caratteri_alfanumerici: alfanumerica = False break # <-- esco dal for # <-- il break mi fa arrivare qui return alfanumerica # testo la funzione print(controlla_alfanumerico("ABC123")) print(controlla_alfanumerico("A!%$*@"))
In alternativa, visto che quando faccio
break
arrivo direttamente alreturn
, posso saltare un passaggio e fare direttamentereturn
:def controlla_alfanumerico(stringa): caratteri_alfanumerici = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" for carattere in stringa: if not carattere.upper() in caratteri_alfanumerici: # ho incontrato un carattere non alfanumerico # posso rispondere False return False # arrivo alla fine del for solo se non ho mai fatto `return`, il # che succede solo se la condizione dell'`if` e' sempre stata False # per tutti i caratteri: vuol dire che sono tutti caratteri alfanumerici # rispondo True return True # testo la funzione print(controlla_alfanumerico("ABC123")) print(controlla_alfanumerico("A!%$*@"))
Soluzione:
def domanda(): percorso = input("scrivi un percorso: ") print(open(percorso).readlines()) # la testo domanda()
Soluzione:
def wc(stringa): num_caratteri = len(stringa) num_a_capo = stringa.count("\n") num_parole = len(stringa.split()) # split rompe sia sugli spazi che sugli a-capo return (num_caratteri, num_a_capo, num_parole) # la testo print(wc("sono\nuna bella\nstringa"))
Soluzione:
def stampa_dizionario(un_dizionario): # l'ordine in cui vanno stampate le righe non importa, percio' # posso usare l'ordine naturale in cui mi vengono fornite da # `items()` for chiave, valore in un_dizionario.items(): print(chiave, "->", (str(valore * 100.0) + "%")) # la testo dizionario = { "arginina": 0.7, "lisina": 0.1, "cisteina": 0.1, "istidina": 0.1, } stampa_dizionario(dizionario)
Soluzione:
def stampa_dizionario_ordinato(un_dizionario): # estraggo le chiavi e le ordino chiavi_ordinate = list(un_dizionario.keys()) chiavi_ordinate.sort() # ora stampo le coppie chiave-valore in ordine for chiave in chiavi_ordinate: valore = un_dizionario[chiave] print(chiave, "->", (str(valore * 100.0) + "%")) # la testo dizionario = { "arginina": 0.7, "lisina": 0.1, "cisteina": 0.1, "istidina": 0.1, } stampa_dizionario_ordinato(dizionario)
Soluzione:
# presa dall'esempio def calcola_fattoriale(n): fattoriale = 1 for k in range(1, n+1): fattoriale = fattoriale * k return fattoriale def crea_lista_di_fattoriali(n): lista_di_fattoriali = [] for i in range(n): lista_di_fattoriali.append(calcola_fattoriale(i)) return lista_di_fattoriali # la testo print(crea_lista_di_fattoriali(2)) print(crea_lista_di_fattoriali(4)) print(crea_lista_di_fattoriali(6))
qui ho riutilizzato la funzione
calcola_fattoriale()
definita in uno degli esempi.Soluzione:
def conta_carattere(testo, carattere_voluto): conta = 0 for carattere in testo: if carattere == carattere_voluto: conta += 1 return conta # la testo print(conta_carattere("abbaa", "a")) print(conta_carattere("abbaa", "b")) print(conta_carattere("abbaa", "?"))
oppure, piu’ semplicemente, posso sfruttare
count()
:def conta_carattere(testo, carattere_voluto): return testo.count(carattere_voluto) # la testo print(conta_carattere("abbaa", "a")) print(conta_carattere("abbaa", "b")) print(conta_carattere("abbaa", "?"))
Soluzione:
def conta_caratteri(testo, caratteri_voluti): conta = {} for carattere_voluto in caratteri_voluti: conta[carattere_voluto] = conta_carattere(testo, carattere_voluto) return conta # la testo print(conta_caratteri("abbaa", "ab?"))
dove ho riutilizzato la funzione dell’esercizio precedente.
Soluzione:
def distanza(coppia1, coppia2): x1, y1 = coppia1 x2, y2 = coppia2 dx = x1 - x2 dy = y1 - y2 return (float(dx)**2 + float(dy)**2)**0.5 # la testo print(distanza((0, 0), (1, 1))) print(distanza((2, 3), (3, 2)))
Soluzione:
def sottostringa(prima, seconda): return seconda in prima # la testo print(sottostringa("ACGT", "T")) print(sottostringa("ACGT", "x"))
Soluzione:
def sottostringhe_non_vuote(stringa): sottostringhe = [] # tutte le possibili posizioni in cui far iniziare la sottostringa for inizio in range(len(stringa)): # tutte le poss. posizioni in cui far finire la sottostringa for fine in range(inizio + 1, len(stringa) + 1): # estraggo la sottostringa ed aggiorno la lista sottostringhe.append(stringa[inizio:fine]) return sottostringhe # la testo print(sottostringhe_non_vuote("ACTG"))
Soluzione:
def conta_sottostringhe(pagliaio, ago): ripetizioni = 0 for inizio in range(len(pagliaio)): for fine in range(inizio+1, len(pagliaio)+1): # stampo quanto vale la sottostringa, per sicurezza print(inizio, fine, ":", pagliaio[inizio:fine], "==", ago, "?") # controllo se la sottostringa e' uguale ad `ago` if pagliaio[inizio:fine] == ago: print("ho trovato una ripetizione!") ripetizioni += 1 return ripetizioni # la testo print(conta_sottostringhe("ACTGXACTG", "ACTG"))
Soluzione:
def sottostringa_piu_lunga(stringa1, stringa2): # riutilizzo la soluzione sopra sottostringhe1 = sottostringhe_non_vuote(stringa1) sottostringhe2 = sottostringhe_non_vuote(stringa2) # controllo tra tutte le coppie di sottostringhe # quale e' quella piu' lunga che appare sia in # stringa1 che in stringa2 piu_lunga = "" for sottostringa1 in sottostringhe1: for sottostringa2 in sottostringhe2: # se sono uguali e piu' lunghe della sottostringa # comune piu' lunga trovata fin'ora... if sottostringa1 == sottostringa2 and \ len(sottostringa1) > len(piu_lunga): # aggiorno piu_lunga = sottostringa1 return piu_lunga # la testo print(sottostringa_piu_lunga("ACTG", "GCTA")) print(sottostringa_piu_lunga("", "ACTG")) print(sottostringa_piu_lunga("ACTG", ""))