Les fonctions
Rappel d'ouverture (5 minutes, cours fermé)
Réponds sans rouvrir les pages précédentes, en écrivant tes réponses.
- Quelle différence entre écrire
elifet écrire un secondifjuste après le premier ? - Écris la boucle qui compte les
"a"de la chaînemot. Où se place l'initialisation du compteur ? - Que vaut
len("bonjour")et que vaut"bonjour"[3]?
Corrigé
- Un
elifn'est testé que si la condition précédente est fausse : les branches s'excluent. Deuxifsuccessifs sont testés tous les deux, et leurs blocs peuvent s'exécuter l'un après l'autre. - L'initialisation est avant la boucle, la mise à jour dedans :
7et"j".
Pourquoi des fonctions ?
En programmation, on a souvent besoin de réutiliser du code, à quelque chose près.
Imagine qu'on doive dessiner un carré et que cela prenne 10 lignes de code. Pour dessiner 50 carrés, faut-il écrire 500 lignes ? Non. On range le code qui dessine un carré dans une fonction, et il suffit ensuite d'une ligne pour l'appeler.
Tu utilises déjà des fonctions écrites par d'autres. print appelle elle-même tout un tas de fonctions, jusque dans les tréfonds de l'ordinateur, pour allumer les bons pixels à l'écran.
Définition : fonction
Une fonction est un bloc de code nommé et réutilisable qui réalise une tâche précise.
Une fonction a :
- un nom ;
- des paramètres ;
- du code à exécuter ;
- et elle peut éventuellement renvoyer une donnée.
1. Avant tout : la signature
La méthode, dans l'ordre
Avant d'écrire la moindre ligne de code, on définit le contrat de la fonction :
- sa signature ;
- sa docstring (ce qu'elle fait) ;
- puis seulement son code ;
- et son
returnsi elle renvoie quelque chose.
Ce que l'IA ne change pas
Une IA écrit le corps d'une fonction en une seconde. Elle sait aussi écrire la signature, la docstring et les tests : ne compte pas sur une incapacité de sa part, il n'y en a pas ici.
Ce qui change, c'est ta position. Pour accepter ou refuser ce qu'elle te propose, il faut que tu saches énoncer le contrat toi-même : que prend la fonction, que renvoie-t-elle, dans quels cas. Sans ce contrat en tête, tu ne peux pas juger le code produit, tu ne peux que le croire. Et tu es évalué sans elle.
Commençons par lire une fonction qui existe déjà, randint, avant d'en écrire une nous-mêmes.
La tâche de randint est de renvoyer un entier pseudo-aléatoire dans un intervalle \([a..b]\).
- elle a un nom :
randint(random integer) ; - elle a 2 paramètres : la borne basse, puis la borne haute ;
- elle exécute du code (rien ne tombe du ciel) ;
- elle renvoie une donnée : le nombre tiré.
Voici comment sa première ligne est écrite :
Définition : la signature
La première ligne d'une fonction est sa signature. Elle comprend :
- le nom de la fonction ;
- la liste des paramètres, entre parenthèses, chacun avec son nom et son type (
nom: type) ; - le type de la valeur renvoyée, après une flèche
->(facultatif mais recommandé).
La ligne juste en dessous est la docstring : une phrase qui décrit ce que fait la fonction. Le code vient ensuite, indenté.
Savoir-faire : écrire une signature
- le mot-clé
defannonce la signature ; - un nom évocateur de la tâche (
randint) ; - les paramètres entre parenthèses, séparés par des virgules, chacun sous la forme
nom: type; - une flèche
->suivie du type de la donnée renvoyée ; - on n'oublie pas les deux-points à la fin.
Cette ligne dit : la fonction s'appelle randint, prend deux entiers a et b, et renvoie un entier.
Prendre du recul : la même idée dans d'autres langages
Signature d'une fonction addition de deux entiers, selon le langage :
Partout, on retrouve un nom, des paramètres et (souvent) un type de retour.
Exercice papier : écrire des signatures et des docstrings
Pour chaque fonction, trouve un nom et des noms de paramètres adéquats, et renseigne bien les types des paramètres et du retour. N'écris pas encore le code, seulement la signature et la docstring.
- Calcule le cube d'un nombre entier.
- Vérifie si un nombre entier est pair.
- Concatène deux chaînes de caractères.
- Calcule la somme des éléments d'une liste d'entiers.
- Vérifie si un élément est présent dans une liste d'entiers.
- Convertit une température de degrés Celsius en degrés Fahrenheit.
- Trouve le plus grand élément d'une liste d'entiers.
- Calcule la moyenne des notes d'une liste de notes.
- Vérifie si un texte est un palindrome.
Aide sur les types, dont un que tu n'as pas encore vu
Une liste de quelque chose est de type list. Une liste d'entiers se note list[int], une liste de chaînes list[str], une liste de listes d'entiers list[list[int]].
Les listes sont étudiées plus loin, dans Les listes : ici, tu n'as pas à savoir les manipuler, seulement à écrire le type dans la signature. C'est justement l'intérêt de l'exercice : on peut énoncer un contrat sur une donnée dont on ne sait pas encore programmer le traitement.
2. Le corps de la fonction et return
Pour coder une fonction, on part de sa signature et du nom de ses paramètres. On considère que la fonction ne connaît que ses paramètres, pas les variables extérieures.
On écrit du code normalement, et on utilise le mot-clé return pour renvoyer le résultat calculé.
def carre(x: float) -> float:
"""Calcule le carré du flottant passé en paramètre"""
resultat = x * x
return resultat
Quand on exécute cette cellule, il ne se passe rien à l'écran : on a seulement appris à Python à calculer un carré. Pour qu'il le fasse, il faut appeler la fonction :
Ce qui se passe vraiment lors d'un appel
Dérouler b = carre(5.3) pas à pas
- Python rencontre l'appel
carre(5.3). Il met la suite de côté et entre dans la fonction. - L'argument
5.3est rangé dans le paramètrex: à l'intérieur,xvaut5.3. - Le corps s'exécute :
resultat = x * x, doncresultatvaut28.09. return resultat: la fonction renvoie28.09et s'arrête aussitôt.- La valeur renvoyée remplace l'appel : la ligne devient
b = 28.09. bvaut donc28.09.
À retenir : un appel de fonction se comporte comme la valeur qu'il renvoie. carre(5.3) finit par « valoir » 28.09, et peut donc s'utiliser partout où une valeur est attendue.
return en bref
- Dès que Python rencontre un
return, il quitte la fonction. - Sans
return, une fonction ne renvoie rien d'utilisable. - Il peut y avoir plusieurs
return, mais quand on peut l'éviter, c'est plus clair.
Piège classique : return n'est pas print
Ces deux fonctions se ressemblent, mais ne font pas la même chose :
def carre_ok(x: float) -> float:
return x * x
def carre_faux(x: float) -> float: # ERREUR fréquente à ne pas reproduire
print(x * x)
carre_ok(5)renvoie25: on peut réutiliser cette valeur,b = carre_ok(5)met25dansb.carre_faux(5)affiche25mais ne renvoie rien :b = carre_faux(5)metNonedansb.
print sert à montrer quelque chose à l'utilisateur. return sert à rendre une valeur au reste du programme. D'ailleurs, print elle-même renvoie None :
Le résultat d'un appel est une valeur comme une autre : on peut l'utiliser dans des calculs.
Prédire avant d'exécuter
Sans lancer le code, prédis ce qui s'affiche, puis vérifie :
(Attention aux parenthèses : un appel de fonction est une valeur, on peut donc l'imbriquer.)
Périmètre
- Écris une fonction qui calcule le périmètre d'un rectangle.
- Calcule le périmètre d'un rectangle de dimensions \(14 \times 50\).
XOR
Le « ou exclusif » (xor) est vrai quand ses deux entrées sont différentes :
c1 |
c2 |
c1 xor c2 |
|---|---|---|
| Vrai | Vrai | Faux |
| Vrai | Faux | Vrai |
| Faux | Vrai | Vrai |
| Faux | Faux | Faux |
Écris une fonction xor qui prend deux booléens et renvoie True ou False selon les cas. Teste-la dans les quatre cas.
3. Fonctions sans valeur de retour
Une fonction peut ne renvoyer aucune valeur : son rôle est alors d'effectuer un travail (afficher, dessiner...) sans rendre d'information. C'est le cas de print.
def dis_coucou(n: int) -> None:
"""Affiche coucou n fois"""
for _ in range(n):
print("Coucou!")
dis_coucou(3)
a = dis_coucou(2)
print(a) # affiche None
Quand une fonction ne renvoie rien, on peut omettre le return. Et une fonction qui ne renvoie « rien » renvoie tout de même quelque chose : None, qui signifie « aucune valeur ».
4. Fonctions sans paramètre
Une fonction peut n'avoir aucun paramètre (comme input()). Il faut alors tout de même écrire les parenthèses vides en signature, pour indiquer qu'il s'agit d'une fonction et non d'une variable.
def demander_prenom() -> str:
"""Demande son prénom à l'utilisateur et le renvoie"""
return input("Ton prénom ? ")
5. Fonctions à plusieurs paramètres
Une fonction peut avoir autant de paramètres qu'on veut. À l'appel, il faut respecter leur nombre et leur ordre.
Rectangle de caractères
L'appel affiche_rectangle(2, 5, 'A') doit afficher :
affiche_rectangle. Rappels : print() passe à la ligne ; print('A', end='') affiche sans passer à la ligne.
Indice léger
Un rectangle, ce sont des lignes ; une ligne, ce sont des caractères. Deux choses à répéter, donc deux boucles, et l'une est à l'intérieur de l'autre. Laquelle dedans ?
Indice plus précis
La boucle extérieure compte les lignes (hauteur tours). La boucle intérieure affiche les caractères d'une ligne (largeur tours), avec end='' pour rester sur la même ligne. Le retour à la ligne se fait avec un print() vide, placé dans la boucle extérieure mais après la boucle intérieure.
Automatismes
Écris la signature, la docstring puis le code de :
nb_secondes(heures, minutes, secondes): renvoie le nombre total de secondes.conversion_euro_dollar(euros): convertit des euros en dollars (taux \(1€ = 1{,}17\$\)). Convertis 12 €.pair_ou_impair(n): renvoie"pair"ou"impair".plus_petit(a, b): renvoie le plus petit des deux entiers.
Corrigé
def nb_secondes(heures: int, minutes: int, secondes: int) -> int:
"""Renvoie le nombre total de secondes"""
return heures * 3600 + minutes * 60 + secondes
def conversion_euro_dollar(euros: float) -> float:
"""Convertit un montant en euros vers des dollars"""
return euros * 1.17
def pair_ou_impair(n: int) -> str:
"""Renvoie 'pair' ou 'impair' selon la parité de n"""
if n % 2 == 0:
return "pair"
return "impair"
def plus_petit(a: int, b: int) -> int:
"""Renvoie le plus petit des deux entiers"""
if a < b:
return a
return b
IMC
- Écris
imc(poids, taille)qui renvoie l'indice de masse corporelle (formule sur Wikipédia). - Écris un programme qui demande poids et taille, puis affiche l'IMC et un message d'interprétation.
- Affiche une réserve claire : cette information ne doit pas servir à un autodiagnostic, il faut préférer l'avis d'un médecin.
Indice léger
La question 1 et la question 2 ne demandent pas la même chose. La fonction calcule et renvoie, elle n'affiche rien et ne demande rien à l'utilisateur. Tout ce qui est saisie et affichage se passe en dehors d'elle.
Indice plus précis
def imc(poids: float, taille: float) -> float: renvoie poids / (taille * taille), avec la taille en mètres. Le programme de la question 2 appelle cette fonction, range le résultat dans une variable, puis enchaîne des elif sur cette variable. Si tu as mis un print dans imc, relis le piège return contre print.
6. Une fonction peut en appeler une autre
La définition d'une fonction peut utiliser une autre fonction, déjà existante ou écrite par toi. C'est ainsi qu'on construit, à partir de briques simples, des fonctions plus puissantes.
def carre(x: float) -> float:
"""Renvoie le carré de x"""
return x * x
def somme_carres(a: float, b: float) -> float:
"""Renvoie la somme des carrés de a et b"""
return carre(a) + carre(b)
print(somme_carres(3, 4)) # 25
Exercice hors ligne : une forêt de sapins (turtle)
Le module turtle ouvre une fenêtre de dessin : cet exercice est à réaliser dans un éditeur Python (Thonny, IDLE...), pas dans le navigateur.
La fonction triangle ci-dessous dessine un triangle isocèle de base horizontale, de sommet \((x, y)\).
import turtle
def triangle(tortue: turtle.Turtle, x: int, y: int, cote: int, couleur: str) -> None:
"""Dessine un triangle isocèle de base horizontale, de sommet (x, y)"""
tortue.penup()
tortue.goto(x, y)
tortue.pendown()
tortue.begin_fill()
tortue.color('black', couleur)
tortue.left(210)
tortue.forward(cote)
tortue.left(150)
tortue.forward(cote * 1.732)
tortue.left(150)
tortue.forward(cote)
tortue.left(210)
tortue.end_fill()
- En utilisant
triangle, écris une fonctionsapin(tortue, x, y)qui empile quelques triangles pour dessiner un sapin. - Écris une fonction
foret(tortue, n)qui dessinensapins à des positions choisies aléatoirement.
Tu sais maintenant écrire une fonction. Reste à s'assurer qu'elle est correcte : c'est l'objet de la page Spécification et tests, qui reprend la docstring et introduit les tests automatiques.