Funzioni di scrittura
Ultimo aggiornamento il 2025-11-06 | Modifica questa pagina
Tempo stimato: 25 minuti
Panoramica
Domande
- Come posso creare le mie funzioni?
Obiettivi
- Spiegare e identificare la differenza tra definizione di funzione e chiamata di funzione.
- Scrivere una funzione che prenda un piccolo numero fisso di argomenti e produca un unico risultato.
Scomporre i programmi in funzioni per renderli più comprensibili.
- Gli esseri umani possono tenere nella memoria di lavoro solo pochi elementi alla volta.
- Comprendiamo idee più grandi/più complesse combinando pezzi.
- Componenti di una macchina.
- Lemmi per la dimostrazione di teoremi.
- Le funzioni hanno lo stesso scopo nei programmi:
- Incapsulare a complessità in modo da poterla trattare come un’unica “cosa”.
- Consentono anche il riutilizzo.
- Scrivere una volta, usare molte volte.
Definire una funzione usando def con un nome, dei
parametri e un blocco di codice.
- Iniziare la definizione di una nuova funzione con
def. - Seguire con il nome della funzione.
- Deve rispettare le stesse regole dei nomi delle variabili.
- Poi i parametri tra parentesi.
- Parentesi vuote se la funzione non riceve input.
- Ne parleremo in dettaglio tra poco.
- Poi i due punti.
- nfine un blocco di codice rientrato.
Definire una funzione non la fa eseguire.
- Definire una funzione non la fa eseguire.
- Come assegnare un valore a una variabile.
- Si deve chiamare la funzione per eseguire il codice che contiene.
OUTPUT
Hello!
Gli argomenti di una chiamata di funzione sono abbinati ai suoi parametri definiti.
- Le funzioni sono più utili quando possono operare su dati diversi.
- Specificare i parametri quando si definisce una funzione.
- Questi diventano variabili quando la funzione viene eseguita.
- Sono assegnati gli argomenti della chiamata (cioè i valori passati alla funzione).
- Se non si nominano gli argomenti quando li si usa nella chiamata, gli argomenti saranno abbinati ai parametri nell’ordine in cui questi sono definiti nella funzione.
PYTHON
def print_date(year, month, day):
joined = str(year) + '/' + str(month) + '/' + str(day)
print(joined)
print_date(1871, 3, 19)
OUTPUT
1871/3/19
Oppure, possiamo dare un nome agli argomenti quando chiamiamo la funzione, il che ci permette di specificarli in qualsiasi ordine e aggiunge chiarezza al sito di chiamata; altrimenti, mentre si legge il codice, si potrebbe dimenticare se il secondo argomento è il mese o il giorno, ad esempio.
OUTPUT
1871/3/19
- Via Twitter:
()contiene gli ingredienti della funzione mentre il corpo contiene la ricetta.
Le funzioni possono restituire un risultato al loro chiamante usando
return.
- Usare
return ...per restituire un valore al chiamante. - Può verificarsi in qualsiasi punto della funzione.
- Ma le funzioni sono più facili da capire se si usa
return:- All’inizio per gestire casi speciali.
- Alla fine, con il risultato finale.
OUTPUT
average of actual values: 2.6666666666666665
OUTPUT
average of empty list: None
- Ricorda: ogni funzione restituisce qualcosa.
- Una funzione che non restituisce esplicitamente un valore
returnrestituisce automaticamenteNone.
OUTPUT
1871/3/19
result of call is: None
Identificare gli errori di sintassi
- Leggete il codice qui sotto e cercate di identificare gli errori senza eseguirlo.
- Eseguite il codice e leggete il messaggio di errore. È un
SyntaxErroro unIndentationError? - Correggere l’errore.
- Ripetere i punti 2 e 3 finché non si sono risolti tutti gli errori.
OUTPUT
calling <function report at 0x7fd128ff1bf8> 22.5
Una chiamata di funzione ha sempre bisogno delle parentesi, altrimenti si ottiene il riferimento all’oggetto funzione. Se volessimo chiamare la funzione report passando il valore 22,5, potremmo scrivere:
OUTPUT
calling
pressure is 22.5
Ordine delle operazioni
- Cosa c’è di sbagliato in questo esempio?
PYTHON
result = print_time(11, 37, 59)
def print_time(hour, minute, second):
time_string = str(hour) + ':' + str(minute) + ':' + str(second)
print(time_string)
- Dopo aver risolto il problema di cui sopra, spiegate perché eseguire questo esempio:
dà questo risultato:
OUTPUT
11:37:59
result of call is: None
- Perché il risultato è
None?
Il problema dell’esempio è che la funzione
print_time()viene definita dopo che la funzione è stata chiamata. Python non sa come risolvere il nomeprint_time, poiché non è ancora stato definito, e solleverà unNameError, ad esempio NameError: name ‘print_time’ is not defined`La prima riga di output
11:37:59è stampata dalla prima riga di codice,result = print_time(11, 37, 59)che lega il valore restituito dall’invocazioneprint_timealla variabileresult. La seconda riga proviene dalla seconda chiamata di stampa per stampare il contenuto della variabileresult.print_time()non restituisce esplicitamentereturnun valore, quindi restituisce automaticamenteNone.
Trovare il primo
Riempite gli spazi vuoti per creare una funzione che prenda come argomento un elenco di numeri e restituisca il primo valore negativo dell’elenco. Cosa fa la funzione se l’elenco è vuoto? E se l’elenco non contiene numeri negativi?
Chiamato per nome
Prima abbiamo visto questa funzione:
PYTHON
def print_date(year, month, day):
joined = str(year) + '/' + str(month) + '/' + str(day)
print(joined)
Abbiamo visto che possiamo chiamare la funzione usando argomenti con nome, in questo modo:
- Che cosa stampa
print_date(day=1, month=2, year=2003)? - Quando avete visto una chiamata di funzione come questa?
- Quando e perché è utile chiamare le funzioni in questo modo?
2003/2/1- Abbiamo visto esempi di utilizzo di argomenti con nome
quando lavoravamo con la libreria pandas. Per esempio, quando si legge
un set di dati usando
data = pd.read_csv('data/gapminder_gdp_europe.csv', index_col='country'), l’ultimo argomentoindex_colè un argomento con nome. - L’uso di argomenti con nome può rendere il codice più leggibile, poiché dalla chiamata di funzione si può vedere quale nome hanno i diversi argomenti all’interno della funzione. Inoltre, può ridurre le possibilità di passare gli argomenti nell’ordine sbagliato, poiché utilizzando argomenti con nome l’ordine non ha importanza.
Incapsulamento di un blocco If/Stampa
Il codice seguente viene eseguito su una stampante di etichette per uova di gallina. Una bilancia digitale comunica al computer la massa di un uovo (in grammi) e il computer stampa un’etichetta.
PYTHON
import random
for i in range(10):
# simulazione della massa di un uovo di gallina
# la massa (casuale) sarà di 70 ± 20 grammi
mass = 70 + 20.0 * (2.0 * random.random() - 1.0)
print(mass)
# il sistema di classificazione stampa un'etichetta
if mass >= 85:
print("jumbo")
elif mass >= 70:
print("large")
elif mass < 70 and mass >= 55:
print("medium")
else:
print("small")
Il blocco if che classifica le uova potrebbe essere utile in altre
situazioni, quindi per evitare di ripeterlo, potremmo ripiegarlo in una
funzione, get_egg_label(). Rivedendo il programma per
utilizzare la funzione si otterrebbe questo:
PYTHON
# revised version
import random
for i in range(10):
# simulazione della massa di un uovo di gallina
# la massa (casuale) sarà di 70 ± 20 grammi
mass = 70 + 20.0 * (2.0 * random.random() - 1.0)
print(mass, get_egg_label(mass))
- Creare una definizione di funzione per
get_egg_label()che funzioni con il programma rivisto sopra. Si noti che il valore di ritorno della funzioneget_egg_label()sarà importante. L’esempio di output del programma precedente sarebbe71.23 large. - Un uovo sporco potrebbe avere una massa superiore a 90 grammi e un
uovo rovinato o rotto probabilmente avrà una massa inferiore a 50
grammi. Modificare la funzione
get_egg_label()per tenere conto di queste condizioni di errore. Un esempio di output potrebbe essere25 too light, probably spoiled.
PYTHON
def get_egg_label(mass):
# Il sistema di classificazione delle uova stampa un’etichetta.
egg_label = "Unlabelled"
if mass >= 90:
egg_label = "warning: egg might be dirty"
elif mass >= 85:
egg_label = "jumbo"
elif mass >= 70:
egg_label = "large"
elif mass < 70 and mass >= 55:
egg_label = "medium"
elif mass < 50:
egg_label = "too light, probably spoiled"
else:
egg_label = "small"
return egg_label
Analisi dei dati incapsulati
Si supponga che sia stato eseguito il seguente codice:
PYTHON
import pandas as pd
data_asia = pd.read_csv('data/gapminder_gdp_asia.csv', index_col=0)
japan = data_asia.loc['Japan']
- Completare le affermazioni seguenti per ottenere il PIL medio del Giappone negli anni riportati per il 1980.
PYTHON
year = 1983
gdp_decade = 'gdpPercap_' + str(year // ____)
avg = (japan.loc[gdp_decade + ___] + japan.loc[gdp_decade + ___]) / 2
- Astrarre il codice precedente in un’unica funzione.
PYTHON
def avg_gdp_in_decade(country, continent, year):
data_countries = pd.read_csv('data/gapminder_gdp_'+___+'.csv',delimiter=',',index_col=0)
____
____
____
return avg
- Come generalizzereste questa funzione se non sapeste in anticipo quali anni specifici sono presenti come colonne nei dati? Per esempio, se avessimo anche i dati degli anni che terminano con 1 e 9 per ogni decennio? (Suggerimento: utilizzate le colonne per filtrare quelle che corrispondono al decennio, invece di enumerarle nel codice)
- Il PIL medio del Giappone negli anni riportati per gli anni ’80 è calcolato con:
PYTHON
year = 1983
gdp_decade = 'gdpPercap_' + str(year // 10)
avg = (japan.loc[gdp_decade + '2'] + japan.loc[gdp_decade + '7']) / 2
- che codifica come funzione è:
PYTHON
def avg_gdp_in_decade(country, continent, year):
data_countries = pd.read_csv('data/gapminder_gdp_' + continent + '.csv', index_col=0)
c = data_countries.loc[country]
gdp_decade = 'gdpPercap_' + str(year // 10)
avg = (c.loc[gdp_decade + '2'] + c.loc[gdp_decade + '7'])/2
return avg
- Per ottenere la media degli anni in questione, è necessario eseguire un ciclo su di essi:
PYTHON
def avg_gdp_in_decade(country, continent, year):
data_countries = pd.read_csv('data/gapminder_gdp_' + continent + '.csv', index_col=0)
c = data_countries.loc[country]
gdp_decade = 'gdpPercap_' + str(year // 10)
total = 0.0
num_years = 0
for yr_header in c.index: # c's index contains reported years
if yr_header.startswith(gdp_decade):
total = total + c.loc[yr_header]
num_years = num_years + 1
return total/num_years
La funzione può ora essere chiamata da:
OUTPUT
20880.023800000003
Simulazione di un sistema dinamico
In matematica, un sistema dinamico è un sistema in cui una funzione descrive la dipendenza temporale di un punto in uno spazio geometrico. Un esempio canonico di sistema dinamico è la mappa logistica, un modello di crescita che calcola una nuova densità di popolazione (tra 0 e 1) in base alla densità attuale. Nel modello, il tempo assume valori discreti 0, 1, 2, …
- Definire una funzione chiamata
logistic_mapche prende due input:x, che rappresenta la popolazione attuale (al tempot), e un parametror = 1. Questa funzione deve restituire un valore che rappresenta lo stato del sistema (popolazione) al tempot + 1, utilizzando la funzione di mappatura:
f(t+1) = r * f(t) * [1 - f(t)]
Utilizzando un ciclo
forowhile, iterare la funzionelogistic_mapdefinita nella parte 1, partendo da una popolazione iniziale di 0,5, per un periodo di tempot_final = 10. Memorizzate i risultati intermedi in una lista, in modo da accumulare, al termine del ciclo, una sequenza di valori che rappresentano lo stato della mappa logistica al tempot = [0,1,...,t_final](11 valori in totale). Stampate questo elenco per vedere l’evoluzione della popolazione.Incapsulate la logica del vostro ciclo in una funzione chiamata
iterateche prende la popolazione iniziale come primo input, il parametrot_finalcome secondo input e il parametrorcome terzo input. La funzione deve restituire un elenco di valori che rappresentano lo stato della mappa logistica al tempot = [0,1,...,t_final]. Eseguire la funzione per i periodit_final = 100e1000e stampare alcuni dei valori. La popolazione sta tendendo verso uno stato stazionario?
PYTHON
initial_population = 0.5
t_final = 10
r = 1.0
population = [initial_population]
for t in range(t_final):
population.append( logistic_map(population[t], r) )
PYTHON
def iterate(initial_population, t_final, r):
population = [initial_population]
for t in range(t_final):
population.append( logistic_map(population[t], r) )
return population
for period in (10, 100, 1000):
population = iterate(0.5, period, 1)
print(population[-1])
OUTPUT
0.06945089389714401
0.009395779870614648
0.0009913908614406382
La popolazione sembra avvicinarsi allo zero.
Utilizzo di funzioni con condizionali in Pandas
Le funzioni contengono spesso dei condizionali. Ecco un breve esempio che indica in quale quartile si trova l’argomento in base ai valori codificati a mano per i punti di taglio del quartile.
PYTHON
def calculate_life_quartile(exp):
if exp < 58.41:
# Questo si trova nel primo quartile
return 1
elif exp >= 58.41 and exp < 67.05:
# Questo si trova nel secondo quartile
return 2
elif exp >= 67.05 and exp < 71.70:
# Questo si trova nel terzo quartile
return 3
elif exp >= 71.70:
# Questo si trova nel quartp quartile
return 4
else:
# Questo deriva da brutti dati
return None
calculate_life_quartile(62.5)
OUTPUT
2
Questa funzione verrebbe tipicamente utilizzata all’interno di un
ciclo for, ma Pandas ha un modo diverso e più efficiente
per fare la stessa cosa, ovvero applicare una funzione a un
dataframe o a una parte di esso. Ecco un esempio, utilizzando la
definizione precedente.
PYTHON
data = pd.read_csv('data/gapminder_all.csv')
data['life_qrtl'] = data['lifeExp_1952'].apply(calculate_life_quartile)
C’è molto in questa seconda riga, quindi esaminiamola pezzo per
pezzo. Sul lato destro di = iniziamo con
data['lifeExp'], che è la colonna del dataframe chiamata
dataetichettata lifExp. Utilizziamo la
apply() per fare ciò che dice, applicare la
calculate_life_quartile al valore di questa colonna per
ogni riga del dataframe.
- Scomporre i programmi in funzioni per facilitarne la comprensione.
- Definire una funzione usando
defcon un nome, dei parametri e un blocco di codice. - Definire una funzione non la esegue.
- Gli argomenti di una chiamata di funzione sono abbinati ai suoi parametri definiti.
- Le funzioni possono restituire un risultato al chiamante usando
return.