Funzioni — una decisione, un nome
Scrivere funzioni con def, cosa separa return da print, gestire i casi limite con una guardia in cima, e docstrings.
Come vuoi imparare?
Il problema che stiamo risolvendo
Due gruppi hanno bisogno della loro media:
marks_a = [72, 45]
marks_b = [90, 33, 61]
print(round(sum(marks_a) / len(marks_a), 2))
print(round(sum(marks_b) / len(marks_b), 2))58.5
61.33Le due righe sono quasi identiche; cambia solo il nome. Un terzo gruppo significa una terza riga, un quarto una quarta.
Il problema non è la digitazione — copiare costa poco. Il problema è che una decisione è ora scritta in diversi posti. Se ti chiedono di mostrare la media a una cifra decimale invece di due, devi trovare e cambiare ogni riga, e dimenticarne una lascia il programma che si comporta in due modi diversi — senza errore.
E una lista vuota? sum([]) / len([]) divide per zero e il programma si ferma. Quel controllo andrebbe aggiunto a ogni riga separatamente.
Una funzione è un modo per scrivere una decisione una volta e darle un nome. Dopo, chiamare il nome fa il lavoro, e cambiarlo significa cambiare un solo posto.
Alla fine di questo capitolo sarai in grado di
- Scrivere e chiamare una funzione con
def - Dire cosa separa
returndaprint— la cosa più importante qui - Tenere una funzione piccola e limitata a un solo lavoro
- Gestire i casi limite con una guardia in cima
- Scrivere una docstring
Prerequisiti: Comprehensions.
Una prima funzione
def average(values):
return round(sum(values) / len(values), 2)
print(average([72, 45]))
print(average([90, 33, 61]))58.5
61.33La forma:
def— "sto definendo una funzione"average— il nome, seguendo le stesse regole di un nome di variabile(values)— il parametro, la porta da cui entrano i dati:e un blocco indentato — il corpo, esattamente comeifoforreturn— restituire un risultato
Scrivere average([72, 45]) è chiamarla. [72, 45] è l'argomento, ciò che viene effettivamente passato; values è il parametro, il nome dentro. Due parole diverse, e entrambe compaiono nei messaggi d'errore, quindi vale la pena tenere la distinzione.
Due righe vuote. Lasciare due righe vuote dopo la definizione di una funzione è la convenzione Python. Python funziona bene anche con meno, ma tutto il codice Python è scritto così, quindi vale l'abitudine.
return e print non sono la stessa cosa
Questa è la sezione più importante del capitolo, e la maggiore confusione per i principianti.
def with_return(x):
return x * 2
def with_print(x):
print(x * 2)
a = with_return(5)
b = with_print(5)
print(a)
print(b)
print(a + 1)10
10
None
11I primi due 10 su schermo fanno sembrare le funzioni identiche. Non lo sono.
with_return ha restituito il valore, quindi a contiene 10 e ci si può aggiungere — ecco l'11 sull'ultima riga.
with_print ha visualizzato il valore e non ha restituito nulla, quindi b è None. Il numero è apparso su schermo, e il programma non ha ricevuto nulla. b + 1 solleverebbe un TypeError.
Tieni la regola così: print è per una persona, return è per il programma. Una funzione che calcola dovrebbe fare return; la visualizzazione avviene dove è stata chiamata. Così la stessa funzione può servire uno schermo oggi e un file domani.
Senza un return, una funzione restituisce silenziosamente None — ed è lo stesso None che append e .sort() hanno restituito nei capitoli dodici e tredici. Ora vedi perché l'hanno fatto: il loro lavoro era cambiare qualcosa, non restituire nulla.
return si ferma immediatamente
def check(mark):
if mark >= 40:
return "pass"
return "fail"
print(check(72))
print(check(20))pass
failNel momento in cui return viene eseguito, la funzione è finita — le righe dopo non vengono mai eseguite. Ecco perché qui non serviva else: se il primo return succede, la seconda riga è irraggiungibile.
Questa forma — restituire non appena una condizione è vera — appiattisce gli alberi if/else profondi del capitolo nove, e rende il codice molto più leggibile.
Gestire i casi limite — una guardia
Ricordi il problema con cui si apriva questo capitolo? La lista vuota:
def average(values):
return sum(values) / len(values)
print(average([]))ZeroDivisionError: division by zeroLa correzione va in cima alla funzione:
def average(values):
if not values:
return 0.0
return round(sum(values) / len(values), 2)
print(average([72, 45]))
print(average([]))58.5
0.0if not values è di nuovo la regola sulla verità del capitolo otto: una lista vuota è falsa, quindi not la rende vera. Non serve scrivere len(values) == 0.
Questo si chiama una guardia: gestisci il caso anomalo in cima, poi scrivi il resto senza preoccuparti. Le funzioni rendono questo economico — il controllo è scritto una volta e chiunque chiami ne beneficia.
0.0è la risposta giusta? Discutibile. Confla "nessuno ha un voto" con "tutti hanno preso zero". I programmi reali spesso restituisconoNone, o sollevano un errore usando ilraisedel capitolo ventiquattro.0.0è tenuto qui per semplicità — ma sappi che è una decisione, non la risposta ovvia.
Docstrings
Un pezzo di testo sulla prima riga della funzione diventa la sua documentazione:
def average(values):
"""Return the mean of values, or 0.0 when there are none."""
if not values:
return 0.0
return round(sum(values) / len(values), 2)
print(average([1, 2, 3]))
print(average.__doc__)2.0
Return the mean of values, or 0.0 when there are none.Ciò che la separa da un commento è che sopravvive nel programma in esecuzione — gli editor la mostrano, e help(average) la stampa.
Una buona docstring dice cosa torna indietro e cosa succede nei casi limite, non come funziona il codice. Il codice è proprio lì sotto.
Un esempio completo
report.py:
# Three small functions, each doing one thing
def average(values):
"""Return the mean of values, or 0.0 when there are none."""
if not values:
return 0.0
return round(sum(values) / len(values), 2)
def grade(mark):
"""Turn a mark into a letter."""
if mark >= 80:
return "A"
if mark >= 70:
return "B"
if mark >= 40:
return "C"
return "F"
def summarise(name, marks):
"""Build one report line for a person."""
mean = average(marks)
return f"{name:<8} {len(marks):>2} papers avg {mean:>6} grade {grade(mean)}"
people = {
"rafi": [72, 88, 61],
"ahmed": [45, 38],
"dia": [],
}
for name, marks in people.items():
print(summarise(name, marks))rafi 3 papers avg 73.67 grade B
ahmed 2 papers avg 41.5 grade C
dia 0 papers avg 0.0 grade FQuattro cose che vale la pena notare.
Ogni funzione fa un lavoro. average calcola la media, grade dà una lettera, summarise costruisce una riga. Nessuna stampa — la stampa avviene nel ciclo in basso, in un posto solo. Quindi scrivere questo report su un file invece significa cambiare quella sola riga.
Le funzioni chiamano funzioni. summarise chiama average e grade. Questo è il vero vantaggio: un lavoro grande assemblato da pezzi piccoli e affidabili.
grade non ha else. Ogni return ferma la funzione, quindi le condizioni restano piatte. La regola del capitolo nove vale ancora: condizione più stretta per prima — mettere 40 prima di 80 darebbe a tutti una C.
La lista vuota di dia non ha rotto nulla. La guardia ha restituito 0.0, grade l'ha trasformato in F, e la riga del report è stata stampata normalmente. Il caso limite è stato gestito una volta, in un posto solo.
Quando si rompe
NameError: name 'greet' is not defined — ma la funzione è proprio lì È stata chiamata prima che la sua riga def venisse eseguita. Python legge un file dall'alto in basso, e il nome non esiste finché il def non viene eseguito. Tieni tutti i def in cima e il codice che li chiama sotto.
TypeError: average() missing 1 required positional argument: 'values' Nessun argomento è stato dato — average() invece di average(marks). Il messaggio nomina persino il parametro mancante.
La funzione restituisce None Non c'è un return, o ce n'è uno all'indentazione sbagliata — dentro un if la cui condizione non è vera, per dire. Un'altra causa comune: print è stato scritto dove serviva return.
Una variabile dentro la funzione non è visibile fuori È previsto — perché è l'argomento del capitolo ventuno. Per ora: fai return di ciò che serve fuori.
SyntaxError — niente due punti sulla riga def Come if e for, una riga def finisce con due punti.
IndentationError: expected an indented block after function definition Un def ha bisogno di almeno una riga indentata dopo.
Passo 4 di 6 — Prevedi
Verifica cosa hai capito
La funzione stampa invece di restituire. Cosa stampano le due righe?
def double(x):
print(x * 2)
result = double(5)
print(result)- A10 None
- B10 10
- CNone 10
- D10
Due return e niente else. Cosa viene stampato?
def check(mark):
if mark >= 40:
return "pass"
return "fail"
print(check(72))- Apass
- Bpass fail
- Cfail
- DNone
La funzione è proprio lì nel file. Cosa succede comunque?
print(greet("rafi"))
def greet(name):
return "hello " + name- AUn `SyntaxError`
- Bhello rafi
- CUn `TypeError`
- DUn `NameError` — la funzione è stata chiamata prima che la sua riga `def` venisse eseguita
Per rispondere serve un account
Accedi per verificare le tue risposte
Le domande sono qui sopra, e ragionarci a mente è la parte che conta. Accedi per vedere le risposte, le spiegazioni e i suggerimenti a tre livelli.
Tocca a te
Scrivi un file chiamato text_tools.py con quattro funzioni, ognuna con una docstring, e nessuna che stampi nulla:
clean(text)— toglie gli spazi e mette in minuscoloinitials(full_name)— trasforma"Rafi Ahmed"in"RA"word_count(text)— conta le parole, dando0per una stringa vuotasummarise(text)— chiama le altre tre e restituisce una riga leggibile
Poi in fondo al file, chiama summarise su alcune stringhe e stampa i risultati — quello è l'unico posto dove qualcosa viene stampato.
Infine, tre esperimenti:
- Chiama
word_count(""). Cosa sarebbe successo senza la guardia? - Sostituisci il
returndisummarisecon unprint, poi memorizza il suo risultato in una variabile e stampa quella. Cosa vedi, e perché? - Sposta la chiamata a
cleansopra il suo stessodef, e leggi l'errore.
Il secondo è i tre minuti più preziosi di questo capitolo. Vedere la differenza tra print e return una volta con i propri occhi significa non confonderli mai più.
Passo 6 di 6
Sfida — il quiz del capitolo
Dieci domande dalla più facile alla più difficile. Le ultime sono difficili di proposito.
Accedi per fare il quiz