Aller au contenu

5. Fonctions⚓︎

Ce chapitre est la pierre angulaire de tout langage informatique !

Pour tous les exercices, vous pouvez valider vos réponses avec le petit gendarme : votre fonction sera alors testée sur des cas de base. Ces cas vous aident à comprendre pourquoi votre fonction n'a pas encore le fonctionnement attendu.

5.1 Principe⚓︎

Pourquoi les fonctions ?

Les lignes suivantes permettent de convertir une température de degré Fahrenheit vers degré Celsius.

temp_fahrenheit = 60
temp_celsius = temp_fahrenheit - 32 * (5/8)
  • Dans le terminal ci-dessous, tester ce programme pour convertir 60 degrés Fahrenheit en degrés Celsius. Afficher la valeur référencée par la variable temp_celsius. Pro tip : dans la console, utiliser la touche Tab pour compléter un mot dont vous aurez écrit le début. temp_f + Tab doit écrire temp_fahrenheit.
  • On souhaite maintenant convertir 90 et 120 degrés Fahrenheit en degrés Celsius. Ajouter les lignes correspondantes.

Solution
temp_fahrenheit = 60
temp_celsius = temp_fahrenheit - 32 * (5/8)
print(temps_celsius)
temp_fahrenheit = 90
temp_celsius = temp_fahrenheit - 32 * (5/8)
print(temps_celsius)
temp_fahrenheit = 120
temp_celsius = temp_fahrenheit - 32 * (5/8)
print(temps_celsius)

On remarque qu'il y a beaucoup de répétitions dans le code, même si nous avons utilisé des variables. Notez que l'on pourrait éventuellement utiliser une boucle :

for temp_fahrenheit in [60, 90, 120]:
    temp_celsius = temp_fahrenheit - 32 * (5/8)
    print(temps_celsius)

Il est toutefois difficile de comprendre ce que fait ce programme.

Le principe DRY va encore être ici à l'oeuvre : la structure fonction va permettre de simplifier un programme en évitant les répétitions inutiles et complexes à débogger. Cette structure va rendre également le code plus abstrait.

Cours

Une fonction permet de créer des blocs d'instructions que nous pouvons réutilisons quand nous le souhaitons. C'est une boite noire indépendante du reste d'un programme :

  • nous pouvons la tester et contrôler son fonctionnement de manière indépendante ;
  • elle découpe le problème en sous-problème plus simple à résoudre ;
  • nous pouvons la réutiliser dans d'autres programmes.
Exemple

Pour construire une voiture, on ne fait pas tout en même temps :

  • on a par exemple besoin de convertir de l'énergie thermique (ou électrique) en énergie mécanique. Pour cela, on "crée" une fonction nommée convertir_énergie_thermique_en_énergie_mécanique qu'on appelle aussi le moteur ;
  • la conversion de l'énergie thermique en énergie mécanique va fonctionner car le moteur a été testé de manière indépendante lors du développement du modèle de voiture ;
  • on le réutilise ensuite pour la production à la chaîne.

Nous nommerons nos fonctions avec des noms d'action : calculer_moyenne, trier_tableau ou afficher_résultats.

5.2 Définir une fonction⚓︎

Cours

Pour définir une fonction en Python, on utilise la syntaxe suivante :

def nom_de_la_fonction(parametre1, parametre2, ... ):
    ... instructions ...
    ... instructions ...
    ... instructions ...
    return résultat1, résultat2, ...

Une fois definie, la fonction appelée nom_de_la_fonction peut être utilisée n'importe où dans le programme.

En Python, l'indentation des instructions appartenant à la fonction est obligatoire.

Important

return est l'instruction permettant de renvoyer des résultats en dehors de la fonction.

Toute variable non renvoyée par return sera perdue dans la boîte noire !

Important

Dans ma grande bonté, je vous propose quelquefois des fonctions où seul le titre es présent (on appelle cela un prototype).

Pour éviter une erreur, le mot-clé pass apparaît dans la fonction : il indique à Python de ne rien faire. Quand vous commencez à écrire la fonction, supprimez pass.

Nous allons tout de suite étudier divers cas particuliers importants.

5.3 Exemple de fonctions⚓︎

5.3.1 Fonction sans paramètres⚓︎

Les fonctions sans paramètres sont des raccourcis pour éviter de retaper plusieurs fois les mêmes instructions. Leur utilité est très limitée : si vous les utilisez, ce sera pour des utilisations très spécifiques.

Exercice

On donne la fonction ci-dessous.

  • Exécuter le programme.
  • Dans la console, écrire compter() (on dit que vous appelez la fonction compter()).
  • Observer le résultat et expliquer celui-ci.
  • Dans la console, faire deux autres appels à la fonction compter(). Que remarquez-vous ?

def compter():bksl-nl for i in range(5):bksl-nl print(i + 1, end = ";")bksl-nl print() # retour à la lignebksl-nl return Nonebksl-nl

Explications

Quand vous avez appelé la fonction dans la console, Python a regardé quelque part dans la mémoire s'il existait une fonction appelée compter(). Il l'a trouvé car nous avons exécuté le programme : ensuite la fonction se déroule.

Cette fonction affiche des informations et ne renvoie aucune valeur : on lui demande de renvoyer la valeur None.

On donne la fonction ci-dessous.

  • Sans l'exécuter, dire ce que permet de faire la fonction écrite dans l'éditeur.
  • Exécuter le programme.
  • Dans la console, afficher la valeur de vitesse_lumière.
  • Modifier le programme en remplaçant la ligne 5 par vitesse_lumière = c. Exécuter le programme et comprendre l'erreur affichée.

def donnerpy-undcélérité():bksl-nl c = 299py-und792py-und458 # en m/sbksl-nl return cbksl-nlbksl-nlvitessepy-undlumière = donnerpy-undcélérité()bksl-nlbksl-nl

Remarque importante

Une variable peut référencer la valeur renvoyée par une fonction. C'est ce que fait : vitesse_lumière = calculer_célérité().

Remarquez toutefois que lorsque vous avez écrit vitesse_lumière = c, vous avez obtenu une erreur : NameError: name 'c' is not defined. Cela signifie que la variable c n'existe pas en dehors de la fonction.

La course des Animaux est un jeu qui permet de déplacer trois types d'animaux sur un plateau :

  • le Zèbre a 1 chance sur 2 de se déplacer de 6 cases ou de reculer d'une case ;
  • le Lion a 1 chance sur 2 de se déplacer de 5 cases ou de ne pas se déplacer du tout ;
  • l'Éléphant a 1 chance sur 4 de se déplacer de 4 cases, et sinon, il se déplace de 2 cases.

En vous inspirant de la fonction ci-dessous, écrire une fonction calculer_déplacement_lion et calculer_déplacement_éléphant permettant de calculer le déplacement du Lion et de l'Éléphant.

b1 = ["calculerpy-unddéplacementpy-undlion() == 5", "calculerpy-unddéplacementpy-undlion() == 0"]bksl-nlb2 = ["calculerpy-unddéplacementpy-undéléphant() == 4", "calculerpy-unddéplacementpy-undéléphant() == 2"]bksl-nlbenchmark = (b1, b2,)bksl-nl 5/5
import randombksl-nlbksl-nldef calculerpy-unddéplacementpy-undzèbre():bksl-nl if random.random() > 1 / 2:bksl-nl déplacement = 6bksl-nl else:bksl-nl déplacement = -1bksl-nl return déplacementbksl-nlimport randombksl-nlbksl-nldef calculerpy-unddéplacementpy-undzèbre():bksl-nl if random.random() > 1 / 2:bksl-nl déplacement = 6bksl-nl else:bksl-nl déplacement = -1bksl-nl return déplacementbksl-nlbksl-nldef calculerpy-unddéplacementpy-undlion():bksl-nl # notation if/else adaptée pour une unique instructionbksl-nl if random.random() > 1 / 2: déplacement = 5bksl-nl else: déplacement = 0bksl-nl return déplacementbksl-nlbksl-nldef calculerpy-unddéplacementpy-undéléphant():bksl-nl # notation if/else adaptée pour une unique instructionbksl-nl if random.random() > 1 / 4: déplacement = 2bksl-nl else: déplacement = 4bksl-nl return déplacementbksl-nl

  • Compléter la fonction placer_aléatoirement. Elle renvoie :
    • un entier aléatoire x compris entre 0 et 700 ;
    • un entier aléatoire y compris entre 0 et 400.
  • Compléter la fonction choisir_couleur. Elle renvoie :
    • 'red' si n vaut 0 ;
    • 'green' si n vaut 1 ;
    • 'blue' si n vaut 2 ;
    • 'purple' si n vaut 3.
  • Exécuter le script. Dans la console, appeler la fonction créer_tache et observer le résultat. Comprendre l'affichage.
  • Dans la console, faire une boucle inconditionnelle appelant 10 fois la fonction créer_tache.

b1 = ("placerpy-undaléatoirement()[0] is not None", "placerpy-undaléatoirement()[1] is not None", "0 <= placerpy-undaléatoirement()[0] <= 700 and 0 <= placerpy-undaléatoirement()[1] <= 400 ")bksl-nlb2 = ("choisirpy-undcouleur() == 'red' or choisirpy-undcouleur() == 'green' or choisirpy-undcouleur() == 'blue' or choisirpy-undcouleur() == 'purple'",)bksl-nlbenchmark = [b1, b2]bksl-nl 5/5
import randombksl-nlimport turtlebksl-nlbksl-nlx = random.randint(0, 700)bksl-nlbksl-nldef placerpy-undaléatoirement():bksl-nl passbksl-nlbksl-nldef choisirpy-undcouleur():bksl-nl n = random.randint(0,3)bksl-nl passbksl-nlbksl-nldef créerpy-undtache():bksl-nl taille = random.randint(1, 50)bksl-nl x, y = placerpy-undaléatoirement()bksl-nl couleur = choisirpy-undcouleur()bksl-nl if couleur is not None:bksl-nl turtle.hideturtle()bksl-nl turtle.penup()bksl-nl turtle.goto(x, y)bksl-nl turtle.pendown()bksl-nl turtle.dot(taille, couleur)bksl-nl return Nonebksl-nlbksl-nlbksl-nlimport randombksl-nlimport turtlebksl-nlbksl-nldef placerpy-undaléatoirement():bksl-nl x = random.randint(0, 700)bksl-nl y = random.randint(0, 400)bksl-nl return x, ybksl-nlbksl-nldef choisirpy-undcouleur():bksl-nl n = random.randint(0,3)bksl-nl if n == 0 : couleur = 'red'bksl-nl if n == 1 : couleur = 'green'bksl-nl if n == 2 : couleur = 'blue'bksl-nl if n == 3 : couleur = 'purple'bksl-nl return couleurbksl-nlbksl-nldef créerpy-undtache():bksl-nl taille = random.randint(1, 50)bksl-nl x, y = placerpy-undaléatoirement()bksl-nl turtle.hideturtle()bksl-nl turtle.penup()bksl-nl turtle.goto(x, y)bksl-nl turtle.pendown()bksl-nl turtle.dot(taille, choisirpy-undcouleur())bksl-nl return Nonebksl-nlbksl-nlfor py-und in range(10):bksl-nl créerpy-undtache()bksl-nl

Aide

Se rappeler la syntaxe des boucles inconditionnelles for _ in range(10).

5.3.2 Fonction avec paramètres⚓︎

Cours

Exemple

  • Exécuter le code.
  • Dans la console, taper compter(5) puis compter(10).

def compter(n):bksl-nl for i in range(n):bksl-nl print(i + 1, end = ";")bksl-nlbksl-nl print() # retour à la lignebksl-nl return Nonebksl-nl

  • La valeur n est appelée paramètre de la fonction compter.
  • On dit qu'on passe le paramètre n à la fonction compter.
  • Lorsque vous avez tapé compter(5) da,ns la console, vous avez appeler la fonction compter avec l'argument 3.
Pour aller plus loin

Les annotations permettent de préciser à un utilisateur extérieur quel est le type des paramètres. Attention, cela n'impose pas le type du paramètres, c'est simplement une indication !

Pour indiquer que n est entier, on peut par exemple écrire :

def compter(n: int):
    ...

Pour plusieurs paramètres a flottant et n entier, on pourrait écrire :

def multiplier(a: float, n: int):
    ...

Nous le reverrons au chapitre 8 !

Exercices

  • Écrire une fonction f qui prend en paramètre un nombre flottant x et qui renvoie le nombre flottant 2 * x + 1.
  • Exécuter votre fonction puis dans la console, écrire quelques tests. Par exemple : f(1).
  • Tester la fonction avec l'icône gendarme.

b1 = ("f(0) = 1", "f(1) == 3", "f(10) == 2py-str10+1",)bksl-nlbenchmark = [b1, ]bksl-nl 5/5
bksl-nlbksl-nlbksl-nlbksl-nldef f(x):bksl-nl return 2 py-str x + 1bksl-nl

  • Compléter la fonction calculer_aire_rectangle qui prend en paramètre deux nombres flottants longueur et largeur et qui renvoie l'aire du rectangle.
  • Ajouter une fonction calculer_volume_boite qui prend en paramètre trois nombres flottants longueur, largeur et hauteur et qui renvoie le volume d'une boite.
  • Exécuter vos fonctions puis dans la console, écrire quelques tests. Par exemple : calculer_aire_rectangle(1, 10).
  • Valider votre résultat avec l'icône gendarme !

b1 = ("calculerpy-undairepy-undrectangle(10, 1) == 10", "calculerpy-undairepy-undrectangle(10, 0) == 0", "calculerpy-undairepy-undrectangle(5, 12) == 60",)bksl-nlb2 = ("calculerpy-undvolumepy-undboite(1, 0, 1) == 0", "calculerpy-undvolumepy-undboite(1, 1, 1) == 1", "calculerpy-undvolumepy-undboite(10, 10, 10) == 1000", "calculerpy-undvolumepy-undboite(5, 4, 2) == 40",)bksl-nlbenchmark = [b1, b2]bksl-nl 5/5
def calculerpy-undairepy-undrectangle(longueur, largeur):bksl-nl passbksl-nlbksl-nlbksl-nlbksl-nldef calculerpy-undairepy-undrectangle(longueur, largeur):bksl-nl aire = longueur py-str largeurbksl-nl return airebksl-nlbksl-nldef calculerpy-undvolumepy-undboite(longueur, largeur, hauteur):bksl-nl volume = longueur py-str largeur py-str hauteurbksl-nl return volumebksl-nlbksl-nlbksl-nl

  • Écrire une fonction est_divisible_par qui prend en paramètre deux nombres entiers positifs entier et diviseur et qui renvoie True si entier est divisible par diviseur.
  • Exécuter votre fonction puis dans la console, écrire quelques tests. Par exemple : est_divisible_par(10, 2).
  • Tester la fonction avec l'icône gendarme.

b1 = ("estpy-unddivisiblepy-undpar(10, 2) == True", "estpy-unddivisiblepy-undpar(10, 3) == False", "estpy-unddivisiblepy-undpar(21, 7) == True",)bksl-nlbenchmark = [b1, ]bksl-nl 5/5
def estpy-unddivisiblepy-undpar(entier, diviseur):bksl-nl passbksl-nlbksl-nlbksl-nlbksl-nldef estpy-unddivisiblepy-undpar(entier, diviseur):bksl-nl if entier % diviseur == 0:bksl-nl return Truebksl-nl else:bksl-nl return Falsebksl-nlbksl-nlbksl-nl

  • Écrire une fonction valider_email qui prend en paramètre une chaîne de caractères email.

    Cette fonction renvoie True si la chaîne de caractères contient un arobase @. Une fois le parcours de l'adresse email avec une boucle inconditionnelle terminée, on renverra False si l'arobase n'a pas été trouvé.

  • Vérifier la fonction en appelant par exemple valider_email("titou_du_01@live.fr").

  • Valider avec l'icône gendarme.

b1 = ("validerpy-undemail('josette@gmail.com')) == True", "validerpy-undemail('@@@@@@protonmail.com')) == True", "validerpy-undemail('libertépy-undégalitépy-undfraternité')) == False",)bksl-nlbenchmark = [b1, ]bksl-nl 5/5
def validerpy-undemail(email):bksl-nl passbksl-nlbksl-nlbksl-nlbksl-nldef validerpy-undemail(email):bksl-nl # on suppose que le caractère @ est absent.bksl-nl trouvé = Falsebksl-nl for lettre in email:bksl-nl if lettre == "@":bksl-nl trouvé = Truebksl-nl return trouvébksl-nlbksl-nlbksl-nlbksl-nl

  • Écrire une fonction sommer qui prend en paramètre un tableau non vide d'entiers tableau. Cette fonction renvoie la somme des entiers présents dans ce tableau.
  • Ajouter une fonction moyenner qui prend en paramètre un tableau non vide d'entiers tableau ainsi que son nombre d'éléments taille. Cette fonction renvoie la moyenne des entiers présents dans ce tableau. Dans cette fonction, on utilisera la fonction sommer définie précédemment.

b1 = ("sommer([10]) == 10", "sommer([10, 2, 6]) == 18", "sommer([8,-8]) == 0",)bksl-nlb2 = ("moyenner([10]) == 10", "moyenner([10, 2, 6]) == 6.0", "moyenner([8,-8]) == 0",)bksl-nlbenchmark = [b1, b2, ]bksl-nl 5/5
def sommer(tableau):bksl-nl passbksl-nlbksl-nlbksl-nlbksl-nldef sommer(tableau):bksl-nl accumulateur = 0bksl-nl for entier in tableau:bksl-nl accumulateur = accumulateur + entierbksl-nl return accumulateurbksl-nlbksl-nldef moyenner(tableau, taille):bksl-nl somme = sommer(tableau)bksl-nl return somme / taillebksl-nlbksl-nl

  • Écrire une fonction trouver_maximum_2 qui prend en paramètre deux entiers a et b. Cette fonction renvoie la valeur du plus grand des deux entiers.
  • Exécuter le programme puis tester votre fonction dans la console avant de le valider avec le gendarme.

b1 = ("trouverpy-undmaximumpy-und2(1, 8) == 8", "trouverpy-undmaximumpy-und2(30, 20) == 30", "trouverpy-undmaximumpy-und2(8, 8) == 8",)bksl-nlbenchmark = (b1, )bksl-nl 5/5
def trouverpy-undmaximumpy-und2(a, b):bksl-nl passbksl-nldef trouverpy-undmaximumpy-und2(a, b):bksl-nl if a > b: bksl-nl maximum = abksl-nl else:bksl-nl maximum = bbksl-nl return maximumbksl-nlbksl-nl

  • Écrire une fonction trouver_maximum_3 qui prend en paramètre deux entiers a, b et c. Cette fonction renvoie la valeur du plus grand des trois entiers. On pourra utiliser la fonction précédente trouver_maximum_2.
  • Exécuter le programme puis tester votre fonction dans la console avant de le valider avec le gendarme.

b1 = ("trouverpy-undmaximumpy-und3(1, 2, 3) == 3", "trouverpy-undmaximumpy-und3(30, 20, 10) == 30", "trouverpy-undmaximumpy-und3(8, 8, 8) == 8",)bksl-nlbenchmark = (b1, )bksl-nl 5/5
def trouverpy-undmaximumpy-und2(a, b):bksl-nl if a > b: bksl-nl maximum = abksl-nl else:bksl-nl maximum = bbksl-nl return maximumbksl-nlbksl-nldef trouverpy-undmaximumpy-und3(a, b, c):bksl-nl passbksl-nl bksl-nldef trouverpy-undmaximumpy-und2(a, b):bksl-nl if a > b: bksl-nl maximum = abksl-nl else:bksl-nl maximum = bbksl-nl return maximumbksl-nlbksl-nldef trouverpy-undmaximumpy-und3(a, b, c):bksl-nl # on veut trouver le maximum M de a et bbksl-nl # puis trouver le maximum entre M et cbksl-nl maximumpy-undactuel = trouverpy-undmaximumpy-und2(a, b)bksl-nl maximum = trouverpy-undmaximumpy-und2(maximumpy-undactuel, c)bksl-nl return maximumbksl-nl

Aide

Pour trouver le maximum entre trois nombres, on veut trouver le maximum M entre deux nombres a et b puis trouver le maximum entre M et le dernier nombre c.

5.3.3 Et si on n'a rien à renvoyer ?⚓︎

Cours

On a toujours quelque chose à renvoyer : en l'absence de valeurs à renvoyer, on écrira, return None.

Pour aller plus loin

En pratique, return None n'est pas obligatoire. Si une fonction ne contient pas le mot-clé return, Python rajoutera gentiment (et automatiquement) return None à la fin de votre fonction lors de l'interprétation du programme.

explicit is better than implicit : écrivez toujours return None.

Exercices

  • Écrire une fonction f qui prend en paramètre un nombre flottant x et qui affiche le nombre flottant x**2 + 1.
  • Réaliser les opérations suivants : y = f(2) puis print(y) ? Expliquer le résultat.

benchmark = []bksl-nl 5/5
bksl-nlbksl-nlbksl-nlbksl-nlbksl-nlbksl-nldef f(x):bksl-nl # on affiche le résultatbksl-nl print(xpy-strpy-str2 + 1)bksl-nl # on renvoie Nonebksl-nl return Nonebksl-nlbksl-nly = f(2)bksl-nlbksl-nlprint(y)bksl-nl

Explications

print(f(2)) affiche :

>>> y = f(2)
5
>>> print(y)
None

L'appel y = f(2) se décompose en :

  • en évaluation du résultat de ma fonction f(2) : c'est un affichage de 2**2 + 1, suivi du renvoi de None comme résultat ;
  • y référence la valeur renvoyée qui est None.

print(y) affiche donc bien None !

Dessin à obtenir

Triangle à tracer

  • Compléter la fonction triangle qui prend en paramètre :

    • une tortue graphique tortue ;
    • la longueur côté du triangle ;
    • l'abscisse initiale x de la tortue ;
    • l'ordonnée initiale y de la tortue.

    Cette fonction dessine un triangle équilatéral de couleur verte dont le sommet en bas à gauche a pour coordonnées \((x, y)\).

  • Exécuter votre programme et tester votre fonction dans la console.

Important

N'oubliez pas le return None !!!

benchmark = []bksl-nl 5/5
import turtlebksl-nlbksl-nlfred = turtle.Turtle()bksl-nlbksl-nldef triangle(tortue, côté, x, y):bksl-nl passbksl-nlbksl-nltriangle(fred, 60, 0, 0)bksl-nlfred.mainloop()bksl-nlimport turtlebksl-nlbksl-nlfred = turtle.Turtle()bksl-nlbksl-nldef triangle(tortue, côté, x, y):bksl-nl tortue.penup()bksl-nl tortue.goto(x, y)bksl-nl tortue.pendown()bksl-nl tortue.fillcolor('green')bksl-nl tortue.beginpy-undfill()bksl-nl for py-und in range(3):bksl-nl tortue.fd(côté)bksl-nl tortue.lt(120)bksl-nl tortue.endpy-undfill()bksl-nl return Nonebksl-nlbksl-nltriangle(fred, 60, 0, 0)bksl-nlfred.mainloop()bksl-nl

Dessin à obtenir

hauteur

  • Écrire une fonction calculer_hauteur qui prend pour paramètre un côté côté et renvoie la hauteur d'un triangle équilatéral de côté c.

  • À l'aide des fonctions calculer_hauteur et triangle, créer la fonction nucléaire ayant pour paramètre une tortue graphique tortue et le côté côté. Le centre du symbole nucléaire est \((0,0)\).

Aide 1

Dessin

hauteur

La hauteur d'un triangle équilatéral peut se calculer facilement en découpant le triangle équilatéral en deux triangles rectangles. On applique alors le théorème de Pythagore. Faites un dessin !!

Aide 2

Les Maths ne sont pas votre truc. Voici la formule : \(h = \dfrac{\sqrt{3}}{2} c\).

Important

Avez-vous pensé au return None !!! J'y tiens. Vraiment. Pour de vrai.

benchmark = []bksl-nl 5/5
import turtlebksl-nlfrom math import sqrtbksl-nlbksl-nlfred = turtle.Turtle()bksl-nlbksl-nldef calculerpy-undhauteur(côté):bksl-nl passbksl-nlbksl-nldef triangle(tortue, côté, x, y):bksl-nl tortue.penup()bksl-nl tortue.goto(x, y)bksl-nl tortue.pendown()bksl-nl tortue.fillcolor('green')bksl-nl tortue.beginpy-undfill()bksl-nl for py-und in range(3):bksl-nl tortue.fd(côté)bksl-nl tortue.lt(120)bksl-nl tortue.endpy-undfill()bksl-nl return Nonebksl-nlbksl-nldef nucléaire(tortue, côté):bksl-nl passbksl-nlbksl-nlnucléaire(fred, 60)bksl-nlfred.mainloop()bksl-nlimport turtlebksl-nlfrom math import sqrtbksl-nlbksl-nlfred = turtle.Turtle()bksl-nlbksl-nldef calculerpy-undhauteur(côté):bksl-nl return sqrt(3) / 2 py-str côtébksl-nlbksl-nldef triangle(tortue, côté, x, y):bksl-nl tortue.penup()bksl-nl tortue.goto(x, y)bksl-nl tortue.pendown()bksl-nl tortue.fillcolor('green')bksl-nl tortue.beginpy-undfill()bksl-nl for py-und in range(3):bksl-nl tortue.fd(côté)bksl-nl tortue.lt(120)bksl-nl tortue.endpy-undfill()bksl-nl return Nonebksl-nlbksl-nldef nucléaire(tortue, côté):bksl-nl hauteur = calculerpy-undhauteur(côté)bksl-nl triangle(tortue, côté, 0, 0)bksl-nl triangle(tortue, côté, -côté, 0)bksl-nl triangle(tortue, côté, -côté / 2, -hauteur)bksl-nl return Nonebksl-nlbksl-nlnucléaire(fred, 60)bksl-nlfred.mainloop()bksl-nl

5.4 Utilisation de return⚓︎

Cours

Dans une seule fonction, il est possible de renvoyer plusieurs valeurs avec return.

Il suffit d'utiliser la syntaxe : return valeur1, valeur2, valeur3 ....

Exemple

import randombksl-nlbksl-nldef générerpy-undcouleurpy-undRGB():bksl-nl """ Génère les composantes Rouge, bksl-nl Vert, Bleu entre 0 et 1. """bksl-nl rouge = random.random()bksl-nl vert = random.random()bksl-nl bleu = random.random()bksl-nl return rouge, vert, bleubksl-nlbksl-nlr, g, b = générerpy-undcouleurpy-undRGB()bksl-nlprint(r, g, b)bksl-nl

Il est également possible de mettre plusieurs return dans une seule fonction.

Dans ce cas, return est comme un siège éjectable : dès que le programme voit une instruction commençant par return, la fonction s'arrête !

Exemple

phrase = "Turning and turning in the widening gyre"bksl-nlbksl-nldef trouverpy-undlettre(lettre, texte):bksl-nl """ Renvoie True si une lettre apparaîtbksl-nl dans un texte """bksl-nl for caractère in texte:bksl-nl print(caractère, lettre, caractère == lettre)bksl-nl if caractère == lettre:bksl-nl return True # la boucle s'arrête !bksl-nl return Falsebksl-nlbksl-nlréponse = trouverpy-undlettre("a", phrase)bksl-nlprint("Trouvé ? ", réponse)bksl-nl

texte est très différent de phrase. En effet, texte et lettre sont les paramètres de la fonction, comme le \(x\) de \(f(x)\) en Maths.

"a" et phrase (ligne 12) sont les arguments de la fonction, comme \(2\) de \(f(2)\) en Maths. Ce sont eux qui vont être analysés par la fonction.

Finalement, caractère est une simple variable de boucle qui n'existe que dans la fonction.

Danger mortel

N'oubliez pas le return ! Si vous n'utilisez que des print, vous ne faîtes que de l'affichage : les résultats de ces fonctions ne pourront en aucun cas être réutilisés dans un autre calcul !

Exercices sur return

On propose ci-dessous la fonction trouver_pair qui prend pour paramètre un tableau d'entiers et qui renvoie

  • le booléen True si un entier pair est présent dans le tableau ;
  • le booléen False sinon.

  • Exécuter le code ;

  • Dans la console, appeler la fonction trouver_pair sur les tableaux [1, 3, 5], [8, 4, 10] et [5, 20, 5]. Que fais réellement cette fonction ?
  • Corriger la fonction pour obtenir le comportement attendu.

b1 = ("trouverpy-undpair([8, 2, 4]) == True", "trouverpy-undpair([7, 5, 1]) == False", "trouverpy-undpair([3, 10, 20, 50]) == True")bksl-nlbenchmark = [b1, ]bksl-nl 5/5
def trouverpy-undpair(tableau):bksl-nl for entier in tableau:bksl-nl if entier % 2 == 0:bksl-nl return Truebksl-nl else:bksl-nl return Falsebksl-nlbksl-nltrouvé = trouverpy-undpair([4, 10, 20])bksl-nlprint(trouvé) bksl-nldef trouverpy-undpair(tableau):bksl-nl for entier in tableau:bksl-nl if entier % 2 == 0:bksl-nl return Truebksl-nl return Falsebksl-nlbksl-nl

  • Écrire une fonction trouver_répétition qui prend pour paramètre une chaîne de caractères lettre et une chaîne de caractères texte.

    Cette fonction renvoie :

    • le booléen True dès que la lettre lettre est répétée plus d'une fois ;
    • le booléen False sinon.
  • Exécuter le code ;

  • Dans la console, à l'aide de la fonction trouver_répétition, vérifier si la lettre "A" est répétée dans la chaîne d'ADN "ACCACGAC", vérifier également que la lettre "G" n'est pas répétée et que la lettre "T" n'est pas répétée.
Aide

Pensez à l'arrêt anticipé d'une boucle à l'aide d'un return.

b1 = ("trouverpy-undrépétition('A', 'AAA') == True", "trouverpy-undrépétition('B', 'AAA') == False", "trouverpy-undrépétition('A', 'ABB') == False",)bksl-nlbenchmark = (b1, )bksl-nl 5/5
def trouverpy-undrépétition(lettre, texte):bksl-nl passbksl-nlbksl-nladn = "ACCACGAC"bksl-nlprint(trouverpy-undrépétition("A", adn))bksl-nlbksl-nldef trouverpy-undrépétition(lettre, texte):bksl-nl compteurpy-undlettre = 0bksl-nl for caractère in texte:bksl-nl if lettre == caractère:bksl-nl compteurpy-undlettre = compteurpy-undlettre + 1bksl-nl if compteurpy-undlettre > 1:bksl-nl return Truebksl-nl # fin de la boucle. On n'a pas trouvé deux occurrences.bksl-nl return Falsebksl-nlbksl-nladn = "ACCACGAC"bksl-nlprint(trouverpy-undrépétition("A", adn))bksl-nl

  • Écrire une fonction translater qui prend pour paramètre deux entiers vec_x et vec_y (coordonnées d'un vecteur) et deux entiers a et b permettant de réaliser une translation de vecteur \((\vec a, \vec b)\). Cette fonction renvoie donc deux entiers résultant de la somme des abscisses et de la somme des ordonnées.
Aide

Pensez à la syntaxe valeur1, valeur2.

b1 = ("translater(0, 0, 0, 0) == (0, 0)", "translater(1, 1, 0, 0) == (1, 1)", "translater(1, 2, 10, 0) == (10, 2)", "translater(1, 2, 10, 9) == (11, 11)")bksl-nlbenchmark = (b1, )bksl-nl 5/5
def translater(vecpy-undx, vecpy-undy, a, b):bksl-nl passbksl-nlbksl-nlvpy-undx, vpy-undy = 2, 3bksl-nltpy-undx, tpy-undy = 1, 0bksl-nlprint(translater(vpy-undx, vpy-undy, tpy-undx, tpy-undy))bksl-nldef translater(vecpy-undx, vecpy-undy, a, b):bksl-nl nouveaupy-undvecpy-undx = vecpy-undx + abksl-nl nouveaupy-undvecpy-undy = vecpy-undy + bbksl-nl return nouveaupy-undvecpy-undx, nouveaupy-undvecpy-undybksl-nlbksl-nlvpy-undx, vpy-undy = 2, 3bksl-nltpy-undx, tpy-undy = 1, 0bksl-nlprint(translater(vpy-undx, vpy-undy, tpy-undx, tpy-undy))bksl-nl

  • Écrire une fonction générer qui prend pour paramètre un entier nombre_bit . Cette fonction génère aléatoirement un nombre binaire de nombre_bit bits, sous forme de chaîne de caractères. On utilisera un accumulateur et on utilisera str(random.randint(0,1)) pour générer des bits aléatoires sous forme de chaîne de caractères.
  • Écrire une fonction décoder qui prend pour paramètre un nombre binaire nombre_binaire écrit sous forme d'une chaîne de caractères. Cette fonction compte le nombre de 0 et le nombre 1 et renvoie quel bit apparaît le plus grand nombre de fois ainsi que le nombre d'apparitions.
Aide 1

On veut ajouter un nouveau bit à l'accumulateur un nombre exact de fois. Utilisez une boucle inconditionnelle.

Aide 2

Pensez à la syntaxe valeur1, valeur2.

b1 = ("décoder('00000') == ('0', 5)", "décoder('01110') == ('1', 3)", "décoder('01010') == ('0', 3)", "décoder('0') == ('0', 1)")bksl-nlbenchmark = (b1, )bksl-nl 5/5
import randombksl-nlbksl-nldef générer(nombrepy-undbit):bksl-nl accumulateur = ""bksl-nl str(random.randint(0,1))bksl-nl passbksl-nlbksl-nldef décoder(nombrepy-undbinaire):bksl-nl compteurpy-und0 = 0bksl-nl compteurpy-und1 = 0 bksl-nl passbksl-nlimport randombksl-nlbksl-nldef générer(nombrepy-undbit):bksl-nl accumulateur = ""bksl-nl for py-und in range(nombrepy-undbit):bksl-nl accumulateur = accumulateur + str(random.randint(0,1))bksl-nl return accumulateurbksl-nlbksl-nldef décoder(nombrepy-undbinaire):bksl-nl compteurpy-und0 = 0bksl-nl compteurpy-und1 = 0 bksl-nl for bit in nombrepy-undbinaire:bksl-nl if bit == '0':bksl-nl compteurpy-und0 = compteurpy-und0 + 1bksl-nl else:bksl-nl compteurpy-und1 = compteurpy-und1 + 1bksl-nl if compteurpy-und1 > compteurpy-und0:bksl-nl return '1', compteurpy-und1bksl-nl else:bksl-nl return '0', compteurpy-und0bksl-nl

5.5 Variables locales et globales⚓︎

Cours

On appelle portée d'une variable l'ensemble des endroits du programme où elle existe.

En théorie, il est possible d'accéder à des variables extérieures à une fonction.

Toutefois, pour simplifier notre travail, nous utiliserons des paramètres pour accéder à ces variables. Cela évite les effets de bord consistant à modifier des variables de manière inattendue.

Pour aller plus loin

Généralement, le langage de programmation cherche les variables en fonction de leur portée.

Ainsi, si une variable appelée dans une fonction n'y apparaît pas, Python va chercher si cette variable apparaît dans une fonction englobante.

Si cette variable n'y apparaît pas non plus, Python va chercher si cette variable apparaît dans le programme principal.

Si la variable est toujours absente, Python va rechercher si cette variable est une variable Python par défaut.

Exercices sur portée des variables

On dispose d'une fonction générer_tableau_entiers.

  • Exécuter le code et dire ce que permet de faire cette fonction ;
  • Modifier le programme afin d'afficher un tableau de 15 nombres aléatoires entre -5 et 5 ;
  • Modifier le programme afin d'afficher deux tableaux : l'un composé de 10 nombres aléatoires entre -5 et 5 et l'autre de 20 nombres aléatoires entre -5 et 5 ;
  • Ajouter un paramètre à la fonction afin de simplifier la réponse à la question précédente.

benchmark = []bksl-nl 5/5
import randombksl-nlbksl-nlnombre = 10bksl-nldef générerpy-undtableaupy-undentiers(entierpy-undmin, entierpy-undmax):bksl-nl tableau = [random.randint(entierpy-undmin,entierpy-undmax) for i in range(nombre)]bksl-nl return tableaubksl-nlbksl-nltableaupy-und1 = générerpy-undtableaupy-undentiers(-5, 5)bksl-nlprint(tableaupy-und1)bksl-nlimport randombksl-nlbksl-nl# Cette fonction permet de générer aléatoirement un tableau contenant un nombre bksl-nl# fixé d'entiers.bksl-nlbksl-nl# nombre doit devenir un paramètre !bksl-nlbksl-nldef générerpy-undtableaupy-undentiers(entierpy-undmin, entierpy-undmax, nombre):bksl-nl tableau = [random.randint(entierpy-undmin,entierpy-undmax) for i in range(nombre)]bksl-nl return tableaubksl-nlbksl-nltableaupy-und1 = générerpy-undtableaupy-undentiers(-5, 5, 10)bksl-nltableaupy-und2 = générerpy-undtableaupy-undentiers(-5, 5, 20)bksl-nlprint(tableaupy-und1)bksl-nlprint(tableaupy-und2)bksl-nl

On dispose de la fonction sum_adder permettant de calculer la somme (sans retenue) d'un additionneur complet 1 bit. a, b et c_0 valent soit 0, soit 1.

  • Pensez-vous que ce code fonctionne ?
  • Exécuter le code et vérifier votre réponse à la question 1 ;
  • Pourquoi ce code est-il compliqué à comprendre ?
  • Corriger la fonction afin d'obtenir un comportement plus prévisible.

benchmark = []bksl-nl 5/5
a = 0bksl-nlb = 0bksl-nldef sumpy-undadder(cpy-und0):bksl-nl return (cpy-und0 + a + b) % 2bksl-nlbksl-nla = 0bksl-nlb = 1bksl-nlsum = sumpy-undadder(0)bksl-nlprint(sum)bksl-nl# a = 0 inutilebksl-nl# b = 0 inutilebksl-nlbksl-nl# a et b deviennent des paramètres.bksl-nldef sumpy-undadder(cpy-und0, a, b):bksl-nl return (cpy-und0 + a + b) % 2bksl-nlbksl-nl# initialisation des variablesbksl-nlbitpy-unda = 0bksl-nlbitpy-undb = 1bksl-nlretenue = 0bksl-nlbksl-nl# appel à la fonction avec 3 paramètresbksl-nlsum = sumpy-undadder(retenue, bitpy-unda, bitpy-undb)bksl-nlprint(sum)bksl-nl

On dispose d'une fonction dessiner_polygone permettant d'ordonner à une tortue tortue de dessiner un polygone de n_côté de longueur L.

  • Exécuter le code ;
  • La tortue fred a-t-elle bien dessiné un polygone à six côtés, de longueur 40 ? Modifier la fonction afin de réaliser cette figure.
  • À la suite de l'hexagone, on souhaite maintenant réaliser un décagone de longueur 40 ainsi qu'un dodécagone de longueur 40. Faire cela en rajoutant deux instruction avant fred.mainloop().

benchmark = []bksl-nl 5/5
import turtlebksl-nlbksl-nldef dessinerpy-undpolygone(tortue, npy-undcôté, L):bksl-nl npy-undcôté = 10bksl-nl angle = 360 // npy-undcôtébksl-nl for py-und in range(npy-undcôté):bksl-nl fred.fd(50)bksl-nl fred.right(angle)bksl-nl return Nonebksl-nlbksl-nlfred = turtle.Turtle()bksl-nldessinerpy-undpolygone(fred, 6, 40)bksl-nlbksl-nlfred.mainloop()bksl-nlimport turtlebksl-nlbksl-nldef dessinerpy-undpolygone(tortue, npy-undcôté, L):bksl-nl angle = 360 // npy-undcôtébksl-nl for py-und in range(npy-undcôté):bksl-nl # attention ! On parle de la tortue en général, pas seulement de fred.bksl-nl tortue.fd(L)bksl-nl tortue.right(angle)bksl-nl return Nonebksl-nlbksl-nlfred = turtle.Turtle()bksl-nldessinerpy-undpolygone(fred, 6, 40)bksl-nldessinerpy-undpolygone(fred, 10, 40)bksl-nldessinerpy-undpolygone(fred, 12, 40)bksl-nlfred.mainloop()bksl-nl

5.6 Documentation d'une fonction⚓︎

Cours

Pour écrire un code facilement compréhensible, nous avons jusqu'à maintenant utilisé le principe de de l'auto-documentation. On nomme toujours clairement nos variables et fonctions.

Exemple

Difficile de savoir ce que fait ce programme...

def f(t):
    m = -1
    for i in t:
        if i > m:
            m = i
    return m 

Beaucoup plus lisible !

def calculer_maximum(tableau):
    maximum_actuel = -1
    for nombre in tableau:
        if nombre > maximum_actuel:
            maximum_actuel = nombre
    return maximum_actuel

Toutefois, il convient également de documenter les fonctions complexes que vous faites.

Documenter permet d'expliquer aux personnes qui ne sont pas familières avec vos fonctions de les comprendre :

  • que fait-elle ?
  • de quels types sont les paramètres ?
  • un petit exemple.

Attention à ne pas trop documenter !

Exemple
def calculer_carré(x):
    """Calcule le carré d'un nombre

    Paramètres :
    x -- flottant ou entier

    Exemple :
    nombre = calculer_carré(2.0)
    """
    return x**2
def calculer_carré(x):
    """Calcule le carré d'un nombre flottant ou entier"""
    return x**2
def f(x):
    return x**2
f = lambda m : (lambda _ : pow(_, 1))(m) * (lambda u : u)(m)

Souvent... Vous allez être la personne nécessitant la documentation. Rien de plus frustrant que de revenir sur un de vos codes non documenté et de passer quelques heures à retrouver comment il fonctionne.

Thor vous apprend à documenter Frodon vous apprend à documenter

Exercice

L'auto-documentation de cette fonction semble suffisante.

À partir de cette auto-documentation, ajouter une documentation adéquate.

b1 = ("décoder('00000') == ('0', 5)", "décoder('01110') == ('1', 3)", "décoder('01010') == ('0', 3)", "décoder('0') == ('0', 1)")bksl-nlbenchmark = (b1, )bksl-nl 5/5
def appartenirpy-unddisque(x, y, xpy-undc, ypy-undc, rayon):bksl-nl if (x - xpy-undc)py-strpy-str2 + (y - ypy-undc)py-strpy-str2 <= rayonpy-strpy-str2:bksl-nl return Truebksl-nl else:bksl-nl return Falsebksl-nlbksl-nlbksl-nldef appartenirpy-unddisque(x, y, xpy-undc, ypy-undc, rayon):bksl-nl """bksl-nl bksl-nl """bksl-nl if (x - xpy-undc)py-strpy-str2 + (y - ypy-undc)py-strpy-str2 <= rayonpy-strpy-str2:bksl-nl return Truebksl-nl else:bksl-nl return Falsebksl-nl

5.7 Résumé⚓︎

Résumé

Dans ce chapitre, j'ai appris :

  • l'intérêt des fonctions en programmation informatique ;
  • à écrire une fonction simple ;
  • la différence entre paramètres et arguments ;
  • l'instruction return et ses subtilités ;
  • à documenter mes fonctions.

Une anecdote

En 1973, un robot est envoyé sur la Lune pour collecter des données. Celui-ci, une fois arrivé, affiche les données sur son écran avec l'équivalent de print.

Rien n'a été renvoyé sur Terre car l'équivalent de return n'a pas été utilisé !

En général, on souhaite faire quelque chose de nos données, pas les afficher.

Retour en haut de la page