Les fonctions — l'abstraction procédurale
Les sous-programmes
Section intitulée « Les sous-programmes »Regarde ce programme de jeu de devinettes, vu dans le chapitre sur les boucles :
import random
print("=== JEU DE DEVINETTES ===")print("Je pense à un nombre entre 1 et 100.")print()
secret = random.randint(1, 100)tentatives = 0
while True: reponse = int(input("Ta proposition : ")) tentatives = tentatives + 1
if reponse < secret: print("Trop petit !") elif reponse > secret: print("Trop grand !") else: print("Bravo ! Tu as trouvé", secret, "en", tentatives, "tentatives !") break
if tentatives <= 3: print("Incroyable !")elif tentatives <= 7: print("Bien joué !")else: print("Tu peux faire mieux !")Ce programme fait plusieurs choses distinctes :
- Afficher un en-tête (lignes 3–4)
- Générer le nombre secret (ligne 7)
- Jouer la partie — la boucle de devinettes (lignes 10–19)
- Évaluer la performance (lignes 21–26)
Chacun de ces blocs est un sous-programme : un morceau de code qui accomplit une tâche précise. Pour l’instant, ils sont tous mélangés dans un seul fichier. Mais que se passe-t-il si tu veux :
- Rejouer une partie sans relancer le programme ?
- Réutiliser l’évaluation de performance dans un autre jeu ?
- Tester la boucle de devinettes indépendamment du reste ?
C’est là qu’interviennent les fonctions : elles permettent de découper un programme en sous-programmes nommés, réutilisables et indépendants.
Des sous-programmes partout
Section intitulée « Des sous-programmes partout »Voici d’autres exemples, tirés des chapitres précédents, où certaines parties du code pourrait être isolé en sous-programme :
Un calcul réutilisable — l’IMC :
# Un calcul réutilisable — l'IMCimc = poids / taille ** 2Une validation de saisie :
# Une validation de saisie — patron qui se répètewhile True: note = float(input("Note (0 à 100) : ")) if note >= 0 and note <= 100: break print("Erreur !")Un traitement dans un menu :
# Chaque option du menu est un sous-programme potentielmatch choix: case "1": km = float(input("Distance en km : ")) milles = km * 0.621371 print(format(km, ".2f"), "km =", format(milles, ".2f"), "milles") case "2": # ...Une formule scientifique :
# Une formule scientifique — Héronimport maths = (a + b + c) / 2aire = math.sqrt(s * (s - a) * (s - b) * (s - c))Dans chaque cas : un bloc cohérent qui prend des données, effectue un traitement, et produit un résultat. C’est exactement la définition d’une fonction.
Définir une fonction avec def
Section intitulée « Définir une fonction avec def »def nom_de_la_fonction(parametre1, parametre2): instructions return resultat- Le mot-clé
def(abréviation de define) - Le nom de la fonction — suit les mêmes règles qu’un nom de variable
- Les parenthèses contenant les paramètres (ou vides si aucun)
- Les deux-points
: - Le corps de la fonction — bloc indenté d’instructions
- Le
return(optionnel) — la valeur renvoyée à l’appelant
Exemple simple : fonction pour saluer
Section intitulée « Exemple simple : fonction pour saluer »def saluer(prenom): print("Bonjour", prenom, "!")
saluer("Alice")saluer("Bob")Résultat de l’exécution
Section intitulée « Résultat de l’exécution »Bonjour Alice !Bonjour Bob !La fonction saluer encapsule un comportement.
On l’appelle autant de fois qu’on veut, avec des arguments différents.
Le code à l’intérieur n’est exécuté que lors de l’appel — la définition seule ne fait rien.
Exemple : fonction qui retourne une valeur
Section intitulée « Exemple : fonction qui retourne une valeur »def calculer_imc(poids, taille): imc = poids / taille ** 2 return imc
# Utilisation (appel de la fonction `calculer_imc`)mon_imc = calculer_imc(70, 1.75)print("Ton IMC est :", format(mon_imc, ".2f"))Résultat de l’exécution
Section intitulée « Résultat de l’exécution »Ton IMC est : 22.86Le return renvoie le résultat au code appelant.
Ce résultat peut être stocké dans une variable, utilisé dans une expression, ou passé à une autre fonction :
# Stocker le résultatmon_imc = calculer_imc(70, 1.75)
# Utiliser directement dans un printprint("IMC :", format(calculer_imc(70, 1.75), ".2f"))
# Utiliser dans une conditionif calculer_imc(70, 1.75) > 25: print("Surpoids")Fonction sans return
Section intitulée « Fonction sans return »Comme mentionné plus haut, une fonction ne retourne pas obligatoirement une valeur.
Si elle n’a pas de return (ou a un return sans valeur), elle retourne None :
def afficher_en_tete(titre): print("=" * 30) print(titre) print("=" * 30)
resultat = afficher_en_tete("MON PROGRAMME")print(resultat) # NoneParamètres obligatoires et optionnels
Section intitulée « Paramètres obligatoires et optionnels »Les paramètres d’une fonction servent à permettre à la fonction de recevoir des informations (valeurs) dont elle a besoin pour effectuer une tâche. Les paramètres sont des variables déclarées dans la définition d’une fonction. Ils permettent de transmettre des données à la fonction au moment où on l’utilise (l’appel de la fonction).
Grâce aux paramètres, une même fonction peut être utilisée avec des valeurs différentes, ce qui la rend plus flexible et réutilisable.
Paramètres obligatoires
Section intitulée « Paramètres obligatoires »Par défaut, tous les paramètres sont obligatoires. L’appel doit fournir exactement le bon nombre d’arguments :
def calculer_imc(poids, taille): return poids / taille ** 2
calculer_imc(70, 1.75) # ✅ 2 arguments pour 2 paramètrescalculer_imc(70) # ❌ TypeError: missing 1 required positional argumentcalculer_imc(70, 1.75, 25) # ❌ TypeError: takes 2 positional arguments but 3 were givenValeurs par défaut
Section intitulée « Valeurs par défaut »On peut donner une valeur par défaut à un paramètre. Si l’argument n’est pas fourni, la valeur par défaut est utilisée :
def saluer(prenom, formule="Bonjour"): print(formule, prenom, "!")
saluer("Alice") # Utilise "Bonjour" par défautsaluer("Bob", "Bonsoir") # Remplace la valeur par défautLe paramètre formule est optionnel : il a une valeur par défaut ("Bonjour"), donc on peut l’omettre à l’appel.
Résultat de l’exécution
Section intitulée « Résultat de l’exécution »Bonjour Alice !Bonsoir Bob !Règle de placement
Section intitulée « Règle de placement »Les paramètres avec valeur par défaut doivent être placés après les paramètres obligatoires :
# ✅ Obligatoires d'abord, optionnels ensuitedef creer_facture(montant, taxe=0.14975, rabais=0): total = montant * (1 + taxe) - rabais return round(total, 2)
# ❌ SyntaxErrordef creer_facture(taxe=0.14975, montant): ...creer_facture(100) # montant=100, taxe=0.14975, rabais=0creer_facture(100, 0.05) # montant=100, taxe=0.05, rabais=0creer_facture(100, 0.14975, 10) # montant=100, taxe=0.14975, rabais=10Portée des variables
Section intitulée « Portée des variables »Variables locales
Section intitulée « Variables locales »Une variable créée à l’intérieur d’une fonction n’existe que pendant l’exécution de cette fonction. C’est une variable locale :
def calculer_aire(rayon): import math aire = math.pi * rayon ** 2 return aire
calculer_aire(5)print(aire) # ❌ NameError: name 'aire' is not definedLa variable aire naît quand la fonction s’exécute et disparaît quand elle se termine.
Les paramètres sont aussi des variables locales :
def doubler(nombre): nombre = nombre * 2 # modifie la copie locale return nombre
x = 5resultat = doubler(x)print(resultat) # 10print(x) # 5 — x n'a pas changé !Quand tu passes x à doubler(), Python crée une copie locale appelée nombre.
Modifier nombre dans la fonction ne touche pas x à l’extérieur.
Variables globales
Section intitulée « Variables globales »Une variable créée en dehors de toute fonction est une variable globale. Elle est accessible en lecture partout, y compris dans les fonctions :
NOM_DU_PROGRAMME = "Calculatrice" # Variable globale
def afficher_en_tete(): print("===", NOM_DU_PROGRAMME, "===") # Lecture d'une variable globale
afficher_en_tete()Cependant, une fonction ne peut pas modifier une variable globale sans le déclarer explicitement :
compteur = 0
def incrementer(): compteur = compteur + 1 # ❌ UnboundLocalErrorIci, Python pense qu’on crée une variable locale compteur, mais on essaie de lire compteur avant de lui assigner une valeur
Constantes globales
Section intitulée « Constantes globales »La seule bonne raison d’utiliser des globales dans les fonctions : les constantes (valeurs qui ne changent jamais).
TAUX_TPS = 0.05TAUX_TVQ = 0.09975
def calculer_taxes(montant): tps = round(montant * TAUX_TPS, 2) tvq = round(montant * TAUX_TVQ, 2) return tps + tvqConvention Python : les constantes sont en MAJUSCULES. La fonction les lit, mais ne les modifie jamais.
Bonnes pratiques
Section intitulée « Bonnes pratiques »Une fonction = une tâche
Section intitulée « Une fonction = une tâche »Chaque fonction devrait remplir une seule tâche. Son nom devrait la décrire.
Problème: Ici analyser_patient FAIT TROP de choses : calcule, catégorise ET affiche
def analyser_patient(poids, taille): imc = poids / taille ** 2 if imc < 18.5: categorie = "Insuffisance" elif imc < 25: categorie = "Normal" elif imc < 30: categorie = "Surpoids" else: categorie = "Obésité" print("IMC :", format(imc, ".2f")) print("Catégorie :", categorie)Solution: Définir des fonctions qui font chacune UNE chose:
def calculer_imc(poids, taille): return poids / taille ** 2
def categorie_imc(imc): if imc < 18.5: return "Insuffisance pondérale" elif imc < 25: return "Poids normal" elif imc < 30: return "Surpoids" else: return "Obésité"Ne rien assumer du reste du programme
Section intitulée « Ne rien assumer du reste du programme »Une fonction ne devrait pas dépendre de variables définies ailleurs (sauf les constantes globales). Tout ce dont elle a besoin doit être passé en paramètres.
Problème: Ici calculer_imc() dépend de variables globales.
poids = 70taille = 1.75
def calculer_imc(): return poids / taille ** 2Solution: Ici calculer_imc() est autonome.
poids = 70taille = 1.75
# Reçoit tout en paramètresdef calculer_imc(poids, taille): return poids / taille ** 2Minimiser les dépendances
Section intitulée « Minimiser les dépendances »Si une fonction a besoin de 8 paramètres, elle fait probablement trop de choses. Découpe-la.
Problème: Trop de paramètres — signal d’alarme
def generer_rapport(nom, prenom, age, note1, note2, note3, programme, session): ...Solution: Découper en fonctions plus petites
def calculer_moyenne(note1, note2, note3): return (note1 + note2 + note3) / 3
def formater_nom(nom, prenom): return prenom + " " + nomNommer clairement
Section intitulée « Nommer clairement »Le nom d’une fonction devrait être un verbe qui décrit ce qu’elle fait :
# ✅ Noms descriptifsdef calculer_imc(poids, taille): ...
def valider_note(note): ...
def afficher_menu(): ...
def convertir_celsius_en_fahrenheit(celsius): ...
# ❌ Noms vagues ou trompeursdef faire_truc(x, y): ...
def imc(p, t): # imc est un nom, pas un verbe ...
def f(a, b, c): # incompréhensible ...| Principe | Explication |
|---|---|
| Une seule tâche | La fonction fait une chose et la fait bien |
| Autonome | Ne dépend pas de variables globales non constantes |
| Paramètres explicites | Tout ce dont elle a besoin est passé en paramètres |
| Retourner plutôt qu’afficher | Fonctions de calcul → return ; print dans l’appelant |
| Peu de paramètres | 3–4 maximum ; au-delà, découper |
| Nom descriptif | Verbe + complément décrivant la tâche |
Mettre tout ensemble : le jeu refactorisé
Section intitulée « Mettre tout ensemble : le jeu refactorisé »Reprenons le programme du début, découpé en fonctions :
import random
# --- Fonctions ---
def afficher_en_tete(): print("=== JEU DE DEVINETTES ===") print("Je pense à un nombre entre 1 et 100.") print()
def jouer_partie(secret): tentatives = 0
while True: reponse = int(input("Ta proposition : ")) tentatives = tentatives + 1
if reponse < secret: print("Trop petit !") elif reponse > secret: print("Trop grand !") else: print("Bravo ! Tu as trouvé", secret, "en", tentatives, "tentatives !") return tentatives
def evaluer_performance(tentatives): if tentatives <= 3: return "Incroyable !" elif tentatives <= 7: return "Bien joué !" else: return "Tu peux faire mieux !"
# --- Programme principal ---
afficher_en_tete()secret = random.randint(1, 100)tentatives = jouer_partie(secret)appreciation = evaluer_performance(tentatives)print(appreciation)Le programme principal se lit maintenant comme un plan : afficher l’en-tête → générer le secret → jouer → évaluer → afficher.
Chaque fonction est testable et réutilisable indépendamment. Et ajouter une boucle « Rejouer ? » devient trivial :
# --- Programme principal ---
afficher_en_tete()
while True: secret = random.randint(1, 100) tentatives = jouer_partie(secret) print(evaluer_performance(tentatives))
encore = input("Rejouer ? (oui/non) : ") if encore != "oui": print("Merci d'avoir joué !") breakSans fonctions, ajouter cette boucle aurait demandé de restructurer tout le programme. Avec des fonctions, c’est quelques lignes de plus.
Bonus : un programme est un sous-programme
Section intitulée « Bonus : un programme est un sous-programme »Pense à un programme Python complet — par exemple, le jeu de devinettes. Il a :
- Des entrées : rien (ou des arguments en ligne de commande)
- Un traitement : la logique du jeu
- Des effets : affichage, saisie
C’est exactement la même structure qu’une fonction. En fait, on pourrait envelopper tout le programme dans une fonction :
import random
def jeu_devinettes(minimum=1, maximum=100): print("=== JEU DE DEVINETTES ===") print("Je pense à un nombre entre", minimum, "et", maximum) print()
secret = random.randint(minimum, maximum) tentatives = 0
while True: reponse = int(input("Ta proposition : ")) tentatives = tentatives + 1
if reponse < secret: print("Trop petit !") elif reponse > secret: print("Trop grand !") else: print("Bravo ! Trouvé en", tentatives, "tentatives !") return tentatives
# Le programme entier devient un appel paramétrablejeu_devinettes() # Partie classique (1 à 100)jeu_devinettes(1, 10) # Partie facilejeu_devinettes(1, 1000) # Partie difficileLe programme est maintenant paramétrable : on change la difficulté sans modifier le code. Dans un projet plus grand, ce fichier pourrait être importé comme un module et appelé par un autre programme.