Fonctions — une décision, un nom
Écrire des fonctions avec def, ce qui sépare return de print, gérer les cas limites avec une garde en haut, et les docstrings.
Comment voulez-vous apprendre ?
Le problème que nous résolvons
Deux groupes ont besoin de leur moyenne :
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.33Les deux lignes sont presque identiques ; seul le nom change. Un troisième groupe signifie une troisième ligne, un quatrième signifie une quatrième.
Le problème n'est pas la saisie — copier coûte peu. Le problème est que une décision est maintenant écrite à plusieurs endroits. Si l'on demande d'afficher la moyenne avec une décimale au lieu de deux, il faut trouver et changer chaque ligne, et en rater une laisse le programme se comporter de deux façons différentes — sans erreur.
Et qu'en est-il d'une liste vide ? sum([]) / len([]) divise par zéro et le programme s'arrête. Cette vérification devrait être ajoutée à chaque ligne séparément.
Une fonction est une façon d'écrire une décision une seule fois et de lui donner un nom. Ensuite, appeler le nom fait le travail, et le changer signifie changer un seul endroit.
À la fin de ce chapitre vous pourrez
- Écrire et appeler une fonction avec
def - Dire ce qui sépare
returndeprint— la chose la plus importante ici - Garder une fonction petite et limitée à un seul travail
- Gérer les cas limites avec une garde en haut
- Écrire une docstring
Prérequis : Compréhensions.
Une première fonction
def average(values):
return round(sum(values) / len(values), 2)
print(average([72, 45]))
print(average([90, 33, 61]))58.5
61.33La forme :
def— « Je définis une fonction »average— le nom, suivant les mêmes règles qu'un nom de variable(values)— le paramètre, la porte par laquelle entrent les données:et un bloc indenté — le corps, exactement commeifouforreturn— renvoyer un résultat
Écrire average([72, 45]) est un appel. [72, 45] est l'argument, ce qui est réellement envoyé ; values est le paramètre, le nom à l'intérieur. Deux mots différents, et les deux apparaissent dans les messages d'erreur, donc la distinction vaut la peine d'être gardée.
Deux lignes vides. Laisser deux lignes vides après une définition de fonction est la convention Python. Python s'exécute correctement avec moins, mais tout le code Python est écrit ainsi, donc cela vaut l'habitude.
return et print ne sont pas la même chose
C'est la section la plus importante du chapitre, et la plus grande confusion des débutants.
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
11Les deux premiers 10 à l'écran donnent l'impression que les fonctions sont identiques. Elles ne le sont pas.
with_return a renvoyé la valeur, donc a contient 10 et on peut l'additionner — d'où 11 sur la dernière ligne.
with_print a affiché la valeur et n'a rien renvoyé, donc b vaut None. Le nombre est apparu à l'écran, et le programme n'a rien reçu. b + 1 provoquerait un TypeError.
Retenez la règle ainsi : print est pour une personne, return est pour le programme. Une fonction qui calcule doit return ; l'affichage se fait là où elle a été appelée. Ainsi la même fonction peut servir un écran un jour et un fichier le lendemain.
Sans return, une fonction renvoie silencieusement None — et c'est le même None que append et .sort() ont renvoyé aux chapitres douze et treize. Maintenant on voit pourquoi ils l'ont fait : leur travail était de changer quelque chose, pas de renvoyer quoi que ce soit.
return s'arrête immédiatement
def check(mark):
if mark >= 40:
return "pass"
return "fail"
print(check(72))
print(check(20))pass
failDès que return s'exécute, la fonction est finie — les lignes après ne s'exécutent jamais. C'est pourquoi aucun else n'était nécessaire ici : si le premier return se produit, la deuxième ligne est inaccessible.
Cette forme — retourner dès qu'une condition correspond — aplatit les arbres profonds if/else du chapitre neuf, et rend le code beaucoup plus facile à lire.
Gérer les bords — une garde
Rappelez-vous le problème qui ouvrait ce chapitre ? La liste vide :
def average(values):
return sum(values) / len(values)
print(average([]))ZeroDivisionError: division by zeroLa correction va au tout début de la fonction :
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 est encore la règle de véracité du chapitre huit : une liste vide est fausse, donc not la rend vraie. Pas besoin d'écrire len(values) == 0.
C'est ce qu'on appelle une garde : traiter le cas anormal en haut, puis écrire le reste sans s'inquiéter. Les fonctions rendent cela peu coûteux — la vérification est écrite une fois et chaque appelant en bénéficie.
Est-ce que0.0est la bonne réponse ? C'est discutable. Cela confond « personne n'a de note » avec « tout le monde a eu zéro ». Les vrais programmes renvoient souventNone, ou lèvent une erreur avec leraisedu chapitre vingt-quatre.0.0est gardé ici pour la simplicité — mais sachez que c'est une décision plutôt que la réponse évidente.
Docstrings
Un bout de texte sur la première ligne de la fonction devient sa documentation :
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.Ce qui la sépare d'un commentaire, c'est qu'elle survit dans le programme en cours d'exécution — les éditeurs l'affichent, et help(average) l'imprime.
Une bonne docstring dit ce qu'elle renvoie et ce qui se passe aux bords, pas comment le code fonctionne. Le code est juste en dessous.
Un exemple complet
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 FQuatre choses méritent d'être notées.
Chaque fonction fait un seul travail. average calcule la moyenne, grade donne une lettre, summarise construit une ligne. Aucune n'imprime — l'impression se fait dans la boucle en bas, en un seul endroit. Donc écrire ce rapport dans un fichier au lieu de l'écran signifie changer cette seule ligne.
Les fonctions appellent des fonctions. summarise appelle average et grade. C'est le vrai bénéfice : un gros travail assemblé à partir de petits morceaux fiables.
grade n'a pas de else. Chaque return arrête la fonction, donc les conditions restent plates. La règle du chapitre neuf s'applique encore : la condition la plus stricte d'abord — mettre 40 avant 80 donnerait C à tout le monde.
La liste vide de dia n'a rien cassé. La garde a renvoyé 0.0, grade a transformé cela en F, et la ligne de rapport s'est imprimée normalement. Le cas limite a été géré une fois, en un seul endroit.
Quand ça casse
NameError: name 'greet' is not defined — mais la fonction est là Elle a été appelée avant sa ligne def. Python lit un fichier de haut en bas, et le nom n'existe pas tant que le def ne s'exécute pas. Gardez tous les def en haut et le code qui les appelle en bas.
TypeError: average() missing 1 required positional argument: 'values' Aucun argument n'a été donné — average() au lieu de average(marks). Le message nomme même le paramètre manquant.
La fonction renvoie None Il n'y a pas de return, ou il y en a un à la mauvaise indentation — à l'intérieur d'un if dont la condition ne correspond pas, par exemple. Une autre cause fréquente : print a été écrit là où return était prévu.
Une variable à l'intérieur de la fonction n'est pas visible à l'extérieur C'est normal — pourquoi est le sujet du chapitre vingt-et-un. Pour l'instant : return tout ce qui est nécessaire à l'extérieur.
SyntaxError — pas de deux-points sur la ligne def Comme if et for, une ligne def se termine par deux-points.
IndentationError: expected an indented block after function definition Un def a besoin d'au moins une ligne indentée après lui.
Étape 4 sur 6 — Prédire
Vérifiez votre compréhension
La fonction imprime au lieu de renvoyer. Que s'affichent les deux lignes ?
def double(x):
print(x * 2)
result = double(5)
print(result)- A10 None
- B10 10
- CNone 10
- D10
Deux return et pas de else. Que s'affiche ?
def check(mark):
if mark >= 40:
return "pass"
return "fail"
print(check(72))- Apass
- Bpass fail
- Cfail
- DNone
La fonction est là dans le fichier. Que se passe-t-il quand même ?
print(greet("rafi"))
def greet(name):
return "hello " + name- AUn `SyntaxError`
- Bhello rafi
- CUn `TypeError`
- DUn `NameError` — la fonction a été appelée avant que sa ligne `def` ne s'exécute
Répondre nécessite un compte
Connectez-vous pour vérifier vos réponses
Les questions sont ci-dessus, et les résoudre de tête est ce qui compte vraiment. Connectez-vous pour voir les réponses, les explications et les indices à trois niveaux.
À vous de jouer
Écrivez un fichier appelé text_tools.py avec quatre fonctions, chacune portant une docstring, et aucune n'imprimant quoi que ce soit :
clean(text)— supprime les espaces et met en minusculesinitials(full_name)— transforme"Rafi Ahmed"en"RA"word_count(text)— compte les mots, donnant0pour une chaîne videsummarise(text)— appelle les trois autres et renvoie une ligne lisible
Puis, en bas du fichier, appelez summarise sur quelques chaînes et imprimez les résultats — c'est le seul endroit où quelque chose est imprimé.
Enfin, trois expériences :
- Appelez
word_count(""). Qu'aurait-il fait sans la garde ? - Remplacez le
returndesummarisepar unprint, puis stockez son résultat dans une variable et imprimez-la. Que voyez-vous, et pourquoi ? - Déplacez l'appel à
cleanau-dessus de son propredef, et lisez l'erreur.
La deuxième est les trois minutes les plus précieuses de ce chapitre. Voir la différence entre print et return une fois de ses propres yeux signifie ne plus jamais les confondre.
Étape 6 sur 6
Défi — le quiz du chapitre
Dix questions, de la plus facile à la plus difficile. Les dernières sont difficiles exprès.
Connectez-vous pour faire le quiz