Écrire des fonctions Python réutilisables
Une fonction Python est un bloc de code nommé que l’on peut appeler pour accomplir une tâche sans réécrire les mêmes instructions. Elle peut recevoir des données en entrée et renvoyer un résultat.
Pour écrire des fonctions Python réutilisables, définissez une opération avec def, transmettez les données par des paramètres et renvoyez le résultat avec return. Précisez ensuite les entrées acceptées et vérifiez quelques cas significatifs. Vous pourrez alors appeler le même calcul avec de nouvelles valeurs, sans recopier ses instructions dans chaque cellule.
Sommaire
Définir des fonctions Python réutilisables avec def et return
Une fonction regroupe des instructions sous un nom que l’on peut appeler. Sa définition indique les paramètres qu’elle reçoit ; son corps décrit le traitement. Dans notre cas, la fonction calcule la moyenne arithmétique d’une liste non vide de nombres : leur somme divisée par leur nombre. La documentation Python sur la définition des fonctions présente cette syntaxe.
# Reprendre les trois premières durées du classeur.
heures_debut = [23, 19, 24]
def moyenne(valeurs):
total = sum(valeurs)
return total / len(valeurs)
# Conserver le résultat pour pouvoir le réutiliser.
moyenne_debut = moyenne(heures_debut)
print(moyenne_debut)22.0def moyenne(valeurs): définit la fonction. Le nom valeurs désigne le paramètre ; les instructions indentées forment le corps. La définition seule ne calcule aucune moyenne. C’est l’appel moyenne(heures_debut) qui exécute ces instructions avec la liste fournie.
sum(valeurs) produit 66 et len(valeurs) produit 3. L’instruction return termine cet appel et renvoie 22.0. L’affectation conserve ce résultat dans moyenne_debut, puis print l’affiche. Le calcul peut ainsi servir à une autre opération, même sans affichage.
![La fonction moyenne reçoit la liste [23, 19, 24] et renvoie 22.0 au code qui l’appelle.](https://mlwebapp.com/wp-content/uploads/2026/10/fonctions-python-entrees-retour-parcours.webp)
return rend le résultat disponible pour la suite du programme.Changer les arguments, conserver le calcul
Un paramètre est un nom déclaré dans la définition de la fonction ; un argument est une valeur fournie lors d’un appel. Dans moyenne(heures_debut), la liste désignée par heures_debut est l’argument associé au paramètre valeurs. Le glossaire Python précise cette distinction.
Reprenons maintenant les huit premières lignes de données du classeur Excel. La liste heures contient leurs durées d’étude ; presences reprend Attendance, le pourcentage de cours suivis. Les deux listes décrivent des grandeurs différentes. On peut leur appliquer le même calcul de moyenne, en conservant leurs unités dans l’affichage.
# Étendre l'extrait aux huit premières lignes du classeur.
heures = [23, 19, 24, 29, 19, 19, 29, 25]
presences = [84, 64, 98, 89, 92, 88, 84, 78]
def moyenne_arrondie(valeurs, ndigits=2):
return round(moyenne(valeurs), ndigits)
# Changer les données sans réécrire le calcul.
print("Heures d'étude :", moyenne_arrondie(heures))
print("Présence (%) :", moyenne_arrondie(presences, ndigits=1))
print("Heures, à une décimale :", moyenne_arrondie(heures, ndigits=1))Heures d'étude : 23.38
Présence (%) : 84.6
Heures, à une décimale : 23.4La fonction moyenne_arrondie appelle moyenne, puis arrondit son résultat. Le paramètre ndigits vaut 2 par défaut : l’appel sur les heures renvoie donc 23.38. Dans moyenne_arrondie(presences, ndigits=1), l’argument nommé fixe ndigits à 1 à la place de sa valeur par défaut. La fonction renvoie alors 84.6.
La troisième sortie, 23.4, montre que le même jeu de valeurs peut être présenté avec un arrondi différent. La moyenne non arrondie des huit durées vaut 23.375.
| Repère | Dans la définition | Dans l’appel |
|---|---|---|
| Données à traiter | valeurs | heures ou presences |
| Option de calcul | ndigits=2 | ndigits=1 pour un autre arrondi |
| Valeur renvoyée | return round(...) | Un nombre que l’appelant peut conserver |
Comprendre la portée locale et les données reçues
La portée d’un nom désigne la partie du programme où ce nom est accessible. Les paramètres et les variables affectées dans une fonction comme celle-ci sont locaux à son appel. Ils peuvent porter le même nom qu’une variable extérieure sans la remplacer.
# Donner le même nom à une variable extérieure pour comparer.
total = 999
def moyenne_locale(valeurs):
total = sum(valeurs)
return total / len(valeurs)
# Vérifier que le total extérieur a conservé sa valeur.
print("Moyenne :", moyenne_locale(heures))
print("Total extérieur :", total)Moyenne : 23.375
Total extérieur : 999Le total utilisé pour la somme est local à moyenne_locale. Le total du notebook vaut toujours 999 après l’appel. Cette affectation locale ne modifie donc pas la variable extérieure. La référence Python sur la résolution des noms décrit les règles de portée.
Une portée locale ne signifie pas que Python copie les objets reçus. Un paramètre peut désigner la même liste que l’argument de l’appelant. Si la fonction y ajoute un élément ou la trie sur place, cette modification reste visible à l’extérieur. Notre calcul de moyenne lit la liste sans la modifier. La leçon sur la mutabilité et les copies approfondit cette distinction.
Distinguer ce qui s’affiche de ce qui est renvoyé
Une fonction qui affiche un nombre ne renvoie pas automatiquement ce nombre à son appelant. Comparons deux comportements sur les mêmes heures :
def afficher_moyenne(valeurs):
print("Moyenne affichée :", moyenne(valeurs))
# Observer ce que reçoit l'appelant après un simple affichage.
resultat_affichage = afficher_moyenne(heures)
print("Valeur reçue :", resultat_affichage)
# Conserver au contraire le nombre renvoyé par le calcul.
resultat_calcul = moyenne(heures)
print("Valeur calculée :", resultat_calcul)Moyenne affichée : 23.375
Valeur reçue : None
Valeur calculée : 23.375afficher_moyenne affiche bien 23.375, mais son corps arrive à la fin sans instruction return. La valeur reçue dans resultat_affichage est alors None. Elle représente ici l’absence de résultat numérique renvoyé. Ce n’est ni une moyenne nulle ni une liste vide.
À l’inverse, moyenne(heures) renvoie le nombre 23.375, qui peut être enregistré, comparé ou transmis à une autre fonction. La référence de l’instruction return détaille le retour d’une fonction.
Pour un calcul réutilisable, renvoyez généralement le résultat et laissez le code appelant choisir sa présentation. Un notebook pourra l’afficher avec print ; une future application pourra l’insérer dans son interface. Une fonction dont le rôle est précisément d’afficher un message peut, elle, se terminer sans renvoyer de valeur utile.

Documenter une fonction et vérifier ses entrées
Le contrat d’une fonction décrit ce qu’elle accepte, ce qu’elle renvoie et les erreurs qu’elle signale. Reprenons le cas de la leçon précédente : des heures arrivent sous forme de chaînes de caractères. La fonction attend une chaîne représentant un entier positif ou nul et renvoie cet entier. Elle refuse les entrées d’un autre type, les chaînes qui ne représentent pas un entier et les valeurs négatives.
def convertir_heures(texte: str) -> int:
"""Convertit une chaîne en nombre entier d'heures positif ou nul.
Lève TypeError si l'entrée n'est pas une chaîne et ValueError si
elle ne représente pas un entier positif ou nul.
"""
# Vérifier le type avant de tenter la conversion.
if not isinstance(texte, str):
raise TypeError("La valeur doit être une chaîne.")
try:
heures = int(texte)
except ValueError:
raise ValueError("Saisissez un entier, par exemple '23'.") from None
# Refuser une valeur incompatible avec le contrat annoncé.
if heures < 0:
raise ValueError("Le nombre d'heures doit être positif ou nul.")
return heures
print(convertir_heures("23"))
print(convertir_heures(" 19 "))23
19La chaîne placée en premier dans le corps est une docstring, ou chaîne de documentation de la fonction. Elle explique le résultat et les refus prévus. Les indications texte: str et -> int sont des annotations de type : elles annoncent une entrée de type chaîne et une sortie entière. Elles ne réalisent pas elles-mêmes une validation à l’exécution. Ce sont les instructions du corps qui appliquent les contrôles.
isinstance(texte, str) vérifie le type reçu avant la conversion. int accepte les espaces autour de " 19 " et renvoie 19. Si la conversion échoue, nous levons une ValueError avec un message adapté à la saisie. from None masque l’exception de conversion d’origine dans le traceback affiché si la nouvelle exception n’est pas interceptée. La nouvelle ValueError reste levée et peut être traitée par le code appelant.
Enfin, la condition heures < 0 protège le sens de la donnée. Accepter 0 est un choix du contrat de cette fonction . Cette validation vérifie seulement le format et le signe.
# Traiter chaque erreur près de l'appel sans arrêter le notebook.
for saisie in [23, "-1", "19.5", ""]:
try:
convertir_heures(saisie)
except (TypeError, ValueError) as erreur:
print(type(erreur).__name__ + " :", erreur)TypeError : La valeur doit être une chaîne.
ValueError : Le nombre d'heures doit être positif ou nul.
ValueError : Saisissez un entier, par exemple '23'.
ValueError : Saisissez un entier, par exemple '23'.L’entier 23 est refusé par TypeError parce que la fonction attend une chaîne. Les chaînes "-1", "19.5" et "" sont refusées par ValueError : elles n’expriment pas un entier positif ou nul. Le try de cette cellule sert à observer ces refus et à poursuivre les exemples. La fonction ne remplace aucune entrée incorrecte par zéro.
Composer des fonctions pour préparer les données et calculer
Refuser une liste vide à l’endroit du calcul
La première version de moyenne divisait directement la somme par le nombre de valeurs. Avec une liste vide, ce nombre vaut zéro. Ajoutons un contrôle explicite avant la division :
def moyenne(valeurs):
# Vérifier le cas vide avant toute division.
if len(valeurs) == 0:
raise ValueError("La liste ne doit pas être vide.")
return sum(valeurs) / len(valeurs)
print(moyenne(heures_debut))
try:
moyenne([])
except ValueError as erreur:
print("ValueError :", erreur)22.0
ValueError : La liste ne doit pas être vide.Cette cellule remplace la définition précédente de moyenne. Une liste non vide de nombres reste l’entrée attendue ; une liste vide lève désormais une ValueError avec un message précis. Renvoyer 0 serait trompeur : aucune moyenne n’a été calculée. Dans ce notebook, moyenne_arrondie utilisera cette nouvelle définition lors de ses prochains appels.
Construire une nouvelle liste sans modifier les saisies
La conversion d’une saisie et la préparation d’une liste ont des rôles différents. La fonction suivante parcourt les chaînes, appelle convertir_heures pour chacune et renvoie une nouvelle liste d’entiers :
def preparer_heures(textes):
# Construire une nouvelle liste sans modifier la liste reçue.
valeurs = []
for texte in textes:
valeurs.append(convertir_heures(texte))
return valeurs
saisies = ["23", "19", "24"]
print(preparer_heures(saisies))
print(saisies)[23, 19, 24]
['23', '19', '24']La première sortie contient les nombres ; la seconde contient toujours les chaînes d’origine. La liste locale valeurs est créée à chaque appel. Si une saisie est refusée, l’exception interrompt cet appel et aucune liste partielle n’est renvoyée. Ce choix évite de calculer silencieusement une moyenne sur moins d’observations que prévu.
Relier les fonctions et réutiliser le traitement
La composition consiste ici à utiliser le résultat d’une fonction dans la suivante. Nous avons besoin de préparer les saisies, puis de calculer leur moyenne arrondie :
def moyenne_heures_saisies(textes, ndigits=2):
# Préparer les saisies avant de réutiliser le calcul.
valeurs = preparer_heures(textes)
return moyenne_arrondie(valeurs, ndigits=ndigits)
# Répéter le même traitement sur deux groupes du même extrait.
print(moyenne_heures_saisies(["23", "19", "24"]))
print(moyenne_heures_saisies(["29", "19", "19", "29", "25"]))22.0
24.2Le premier appel reprend les trois premières durées et renvoie 22.0. Le deuxième utilise les cinq suivantes, soit 29, 19, 19, 29 et 25, et renvoie 24.2. Les données changent, les règles de conversion et de calcul restent identiques.
Chaque contrôle reste à l’endroit où il est utile : convertir_heures traite une saisie, preparer_heures construit la liste, moyenne refuse le vide et moyenne_arrondie applique l’arrondi. moyenne_heures_saisies les relie. Cette décomposition permet de modifier une règle sans réécrire tout le traitement.

Vérifier le résultat et les refus prévus
Un test compare le comportement obtenu à celui qui est attendu. Les appels précédents rendent les résultats visibles ; quelques assertions permettent de contrôler que ces résultats restent corrects après une modification.
# Vérifier les résultats et la conservation des textes d'origine.
assert moyenne(heures_debut) == 22.0
assert moyenne_arrondie(heures) == 23.38
assert convertir_heures(" 0 ") == 0
assert preparer_heures(["23", "19", "24"]) == [23, 19, 24]
assert moyenne_heures_saisies(["23", "19", "24"]) == 22.0
assert saisies == ["23", "19", "24"]
# Vérifier que la fonction composée transmet les erreurs prévues.
try:
moyenne_heures_saisies([])
except ValueError as erreur:
assert str(erreur) == "La liste ne doit pas être vide."
else:
raise AssertionError("La liste vide aurait dû être refusée.")
try:
moyenne_heures_saisies(["23", "-1"])
except ValueError as erreur:
assert str(erreur) == "Le nombre d'heures doit être positif ou nul."
else:
raise AssertionError("La valeur négative aurait dû être refusée.")
try:
moyenne_heures_saisies(["23", "19.5"])
except ValueError as erreur:
assert str(erreur) == "Saisissez un entier, par exemple '23'."
else:
raise AssertionError("La saisie décimale aurait dû être refusée.")
print("Tests réussis.")Tests réussis.assert lève une AssertionError si la condition est fausse. Le message « Tests réussis. » n’apparaît que lorsque les vérifications précédentes sont passées. Le bloc try vérifie également qu’une liste vide est effectivement refusée ; sa branche else signale l’absence inattendue d’exception.
Exercice : compter les valeurs qui atteignent un seuil
Écrivez une fonction compter_au_moins(valeurs, seuil) qui reçoit une liste de nombres et renvoie le nombre d’éléments supérieurs ou égaux au seuil. Elle doit renvoyer 0 pour une liste vide et laisser la liste reçue inchangée. N’ajoutez aucun affichage dans la fonction.
# Préparer les données et le seuil à utiliser dans votre fonction.
heures_exercice = [23, 19, 24, 29, 19, 19, 29, 25]
seuil_exercice = 24
print(heures_exercice)
print("Seuil en heures par semaine :", seuil_exercice)[23, 19, 24, 29, 19, 19, 29, 25]
Seuil en heures par semaine : 24Sur ces huit durées d’étude, le résultat doit être 4 pour le seuil 24. Contrôlez aussi [23, 24, 25] avec le même seuil : le résultat doit être 2, puisque la valeur égale au seuil compte. Ce seuil sert uniquement à compter des durées dans l’extrait ; il ne définit aucune catégorie de réussite.
Afficher la correction et ses vérifications
def compter_au_moins(valeurs, seuil):
# Compter sans modifier les valeurs de départ.
compteur = 0
for valeur in valeurs:
if valeur >= seuil:
compteur += 1
return compteur
# Vérifier aussi la liste vide et la valeur exactement au seuil.
assert compter_au_moins(heures_exercice, 24) == 4
assert compter_au_moins([], 24) == 0
assert compter_au_moins([23, 24, 25], 24) == 2
print(compter_au_moins(heures_exercice, seuil_exercice))
print("Tests de la correction réussis.")4
Tests de la correction réussis.Le compteur commence à zéro à chaque appel. La boucle ajoute 1 au compteur lorsque valeur >= seuil. Le return se trouve après la boucle : tous les éléments ont donc été examinés avant de renvoyer le total. Sur une liste vide, la boucle n’exécute aucune itération et le compteur reste à zéro.
Corriger les erreurs fréquentes dans une fonction
- Un nombre s’affiche, mais la variable contient
None. Vérifiez qu’unreturnrenvoie le résultat sur le chemin exécuté. Un affichage avecprintne suffit pas. - Le comptage s’arrête au premier élément. Un
returnplacé dans la boucle termine immédiatement l’appel. - La fonction utilise encore une ancienne liste. Vérifiez qu’elle lit son paramètre plutôt qu’une variable extérieure. Puis redémarrez le noyau et exécutez les cellules dans l’ordre.
- Une liste change après l’appel. Cherchez une modification sur l’objet reçu, par exemple un ajout ou un tri sur place. Décidez explicitement si la fonction doit modifier l’entrée ou produire un nouvel objet.
- Un appel lève une
TypeErrorsur les arguments. Comparez les noms et le nombre d’arguments à la définition. Dans notre conversion, un entier reçu à la place d’une chaîne est aussi un refus volontaire.
À retenir
Une fonction réutilisable expose les données et options nécessaires dans ses paramètres, renvoie un résultat exploitable et précise les conditions de son utilisation. Sa docstring aide à comprendre ce contrat ; les contrôles et les tests vérifient son application. Vous avez maintenant séparé la préparation des saisies, le calcul et l’affichage, tout en conservant les données d’origine.