Blog ENI : Toute la veille numérique !
🎁 Jusqu'au 25/12 : 1 commande de contenus en ligne
= 1 chance de gagner un cadeau*. Cliquez ici
🎁 Jusqu'au 31/12, recevez notre
offre d'abonnement à la Bibliothèque Numérique. Cliquez ici
  1. Livres et vidéos
  2. Python
  3. Les fonctions
Extrait - Python Apprenez à développer des projets ludiques (3e édition)
Extraits du livre
Python Apprenez à développer des projets ludiques (3e édition)
2 avis
Revenir à la page d'achat du livre

Les fonctions

Introduction

Dans ce chapitre, nous vous présentons les fonctions, notion essentielle de la programmation. Pour utiliser les fonctions sereinement, il faut maîtriser le passage des arguments qui repose sur la gestion des associations entre variables. La mécanique des associations est souvent délaissée durant l’apprentissage du langage Python, car considérée comme trop technique, et son utilité n’est pas vraiment apparente. Cependant, sans connaître ce mécanisme, vous foncez droit vers l’accident. Le langage Python a la réputation d’être un langage facile. Pourtant, il ne l’est pas. Sa syntaxe est simple, certes, ce qui permet un apprentissage rapide. Cela donne l’impression qu’écrire un programme est devenu une chose aisée. Au démarrage, tout se passe correctement. Cependant, assez rapidement, un comportement étrange va se produire et le problème apparaîtra 99 % du temps suite à l’utilisation d’une liste :

  • La liste affichée ne contient pas les bons éléments. Pourtant, elle a été modifiée ! Un point d’arrêt en mode Debug vous confirme que l’interpréteur a bien exécuté les lignes chargées de la modifier. Pourtant, plus tard, durant l’exécution, les nouvelles valeurs ont disparu...

Association entre variables

1. Le mécanisme des associations

Rappelons pour commencer le mécanisme d’association des listes :

L = [ 4,5,6] 
K = L 
L[1] = 9 

Les variables K et L sont associées au même objet liste, elles sont donc liées. Si on modifie l’élément L[1], on modifie aussi K[1] :

images/05RI01.png
images/05RI02.png

Mais qu’en est-il pour les variables de type entier ? Lors de l’exécution d’un programme Python, toute variable est associée à une chose. Techniquement, on parle d’objet. Un objet peut être un nombre, une chaîne de caractères, un fichier ou une image. Ainsi, lorsque nous écrivons :

a = 3 
b = a 

les variables a et b, même si elles portent des noms différents, sont associées au même objet 3. La fonction native id() donnant le numéro d’identification de l’objet associé à une variable confirme cette affirmation :

print(id(a)) 
print(id(b)) 
>> 140710295855824 
>> 140710295855824 

Nous allons examiner ce qu’il se passe pas à pas. La première ligne crée la variable a et l’associe au chiffre 3. La deuxième ligne crée la variable b et l’associe à l’objet 3 déjà associé à la variable a. À cet instant, et uniquement à cet instant, les deux variables sont liées. Ainsi, nous avons en mémoire :

images/05RI03.png

Maintenant, effectuons l’opération suivante :

b = 2 + b 

Il est évident que b vaut 5 à la fin de cette ligne. Mais comme a et b étaient liées, que vaut a : 5 ou 3 ? Si on pense au mécanisme des listes, nous serions tentés de dire 5. Pourtant, le bon sens nous amènerait à dire 3… Examinons pas à pas ce qu’il se passe sur cette ligne. Lorsque nous écrivons 2+b, nous devons évaluer une expression dont le résultat donne une nouvelle valeur 5. À ce niveau, il reste à effectuer l’affectation b = 5. Cette action associe la variable b à la valeur 5. Ainsi, les deux variables ne sont plus liées :

images/05RI04.png

L’affectation b = a a lié momentanément ces deux variables à la même valeur. Mais à la troisième ligne, cette liaison est rompue. Maintenant, a et b sont deux variables différentes.

Python Tutor ne représente pas les nombres par des associations. Nous avons un peu triché dans le précédent schéma. Habituellement, les valeurs désignant des nombres sont accolées aux noms des variables.

Et si nous pouvions modifier l’objet désignant le chiffre 3 pour qu’il vaille 4 par exemple, que vaudraient les variables a et b? Dans ce cas, si les variables a et b sont liées au même objet et si cet objet est modifié, les variables a et b désigneront alors cette même valeur 4. Mais ! Une telle fonction n’existe pas ! En effet, les affectations compactes comme a += 1 sont le raccourci...

Intérêt des fonctions

1. Rôles et définition

Les fonctions permettent de créer du code réutilisable, ce qui représente une approche intéressante. De plus, elles permettent d’associer un nom à une partie du code, nom qui idéalement doit décrire au mieux le traitement effectué.

Nous pouvons toujours essayer d’écrire un programme sans utiliser de fonctions, c’est possible… Mais si le programme devient long, la gestion du code va devenir fatigante. Cependant, même lorsque le besoin d’écrire une fonction se fait ressentir, vous serez tenté par le côté obscur ! Il vous poussera à effectuer un copier-coller afin de vous éviter l’écriture d’une fonction supplémentaire. C’est si facile et si vite fait un copier-coller ! En plus, la plupart du temps, cela marchera du premier coup. Comment y résister ! Prenons l’exemple du code suivant :

TotalTTC = 0 
Prix1HT = 18 
TVA = 0.2 
Prix1TTC = Prix1HT*(1+TVA) 
TotalTTC = TotalTTC + Prix1TTC 
Prix2HT = 21 
Prix2TTC = Prix2HT*(1+TVA) 
TotalTTC = TotalTTC + Prix2TTC 
print(TotalTTC) 

Le code est lisible. Les variables sont correctement nommées. Cependant, pour savoir ce que fait ce bout de code, il faut un peu se concentrer pour vérifier les actions effectuées à chaque ligne. Il semble que nous effectuons deux fois le calcul d’un prix TTC à partir d’un prix HT. Nous allons améliorer la structure de notre code en créant une fonction pour cette tâche :

def PrixTTC(prixHT): 
  TVA = 0.2 
  return Prix1HT * (1 + TVA) 
 
Prix1HT = 18 
Prix2HT = 21 
 
TotalTTC = 0 
TotalTTC = TotalTTC + PrixTTC(Prix1HT)  
TotalTTC = TotalTTC + PrixTTC(Prix2HT)  
 
print(TotalTTC) 

La déclaration ou définition d’une fonction se fait par l’utilisation du mot-clé def. La syntaxe d’une déclaration doit respecter l’ordre suivant :

  • mot-clé def

  • nom de la fonction : PrixTTC

  • parenthèse ouvrante : (

  • noms des paramètres séparés par des virgules s’il y en a plusieurs

  • parenthèse fermante : )

  • fin de la déclaration par l’utilisation d’un deux-points à la fin de la ligne

def NomFonction(param1,param2,...) : 

Le mot-clé def crée un sous-bloc contenant le code de la fonction. Ainsi, l’écriture du corps de la fonction a une indentation décalée par rapport au mot-clé def. Lorsque ce décalage prend fin, cela signifie que le bloc de la fonction est terminé. Le mot-clé return est utilisé pour renvoyer le résultat calculé par la fonction.

La déclaration est une description. Ainsi à partir du mot-clé def, on décrit la fonction PrixTTC(), c’est-à-dire que l’on donne ses paramètres et les opérations qu’elle doit effectuer lorsqu’on l’appelle. Mais, attention, aucune action n’est effectuée, aucun traitement n’est lancé. On définit ce que doit faire la fonction PrixTTC() si on l’appelle. Si aucun appel n’est...

Gestion des variables

1. Les variables globales

Une variable globale est une variable définie dans le bloc principal du programme. Une variable globale est utilisable uniquement après sa création. Toute tentative d’utilisation avant sa définition se solde par un message d’erreur. Nous présentons un exemple ci-après. À la première ligne, nous créons la variable a et lui affectons la valeur 1. Sur l’ensemble des lignes qui suivent, nous pouvons donc utiliser ce nom sans produire d’erreur. Ainsi, à la ligne 2, l’instruction print(a) affiche la valeur 1. À la troisième ligne, nous avons l’instruction print(b). Le nom b est alors inconnu car la variable b n’existe pas encore.

Le programme s’arrête et affiche un message d’erreur indiquant que le nom b n’est pas défini : "name ’b’ is not defined". La variable b est créée à la ligne suivante, elle n’était pas encore utilisable à ce niveau. La dernière instruction print(b) affiche la valeur de b, c’est-à-dire 2.

a = 1 
print(a) # 1 
print(b) # erreur : NameError: name 'b' is not defined 
b = 2 
print(b) # 2 
Les résultats de vos programmes peuvent varier si vous travaillez en mode Python interactif. En effet, dans ce mode les variables sont conservées d’un lancement à l’autre. Vous pouvez donc n’avoir aucun message d’erreur, car la variable b provient d’un exercice précédent. Pour tester cet exemple, vous pouvez relancer l’environnement Python pour "vider" l’ensemble des variables. Si vous travaillez avec Pyzo, pensez à cliquer sur l’icône de rechargement images/05RI15.png de l’environnement Python.

2. Les variables locales

Une variable locale est une variable créée à l’intérieur d’une fonction. On dit qu’elle est locale à cette fonction, car elle est utilisable uniquement à l’intérieur du bloc de la fonction. Une fois la fonction terminée, le nom de cette variable disparaît. Cela sous-entend que l’on ne peut pas l’utiliser ailleurs. Les variables locales, comme les variables globales, sont utilisables uniquement après leur définition. Étudions maintenant le cas ci-après.

a = 1 
 
def test(b): 
  print(b) 
  print(c) # erreur, cette ligne doit être supprimée 
  c = 3 
  print(c) 
  print(b) 
test(a) 
print(c)  # erreur : c est inconnue 
print(b)  # erreur : b est inconnue 

Lors de l’appel de la fonction test(), nous donnons l’argument a, c’est-à-dire la valeur 1, au paramètre b. Gardez ceci en mémoire.

Règle : un paramètre définit une variable locale en début de fonction.

Ainsi, l’instruction print(b) se trouvant en première ligne du bloc de la fonction produit l’affichage de la variable b, c’est-à-dire de la valeur 1. L’instruction suivante : print(c) ne peut fonctionner car aucune variable c n’existe encore. Il faut supprimer cette ligne. La ligne suivante définit...

Exercices d’application

1. Copies et modifications de listes

Donnez l’état de la mémoire après l’exécution du programme suivant :

a = [ 1,2,3] 
b = a.copy() 
c = b 
a[0] = 10 
b[1] = 11 
c[2] = 12 

Correction guidée :

La première ligne crée une liste initialisée avec les valeurs 1, 2 et 3. La deuxième initialise la variable b avec une copie de la liste a. Grâce à cette fonction copy(), les deux listes sont identiques, mais indépendantes en mémoire :

images/05RI20.png

L’affectation c = b a pour effet d’associer la variable c à la même liste que la variable b. À ce niveau, les variables b et c sont liées, elles désignent la même liste :

images/05RI21.png

En écrivant a[0] = 10, nous positionnons le premier élément de la liste associée à la variable a à la valeur 10. Les deux écritures b[1] = 11 et c[2] = 12 modifient les deux dernières cases de la liste du bas :

images/05RI22.png

À ce niveau, si nous faisons print(a,b,c), nous obtenons :

>> [10,2,3] [1,11,12] [1,11,12] 

2. Création de liste de listes I

Donnez l’état de la mémoire et les affichages après l’exécution du programme :

R = [0,0,0] 
L = [ R,R,R] 
L[0][1] = 5 
 
S = [ [0,0,0], [0,0,0], [0,0,0] ] 
S[0][1] = 5 
 
print(L) 
print(S) 

Correction guidée :

La première ligne crée une liste remplie avec trois fois la valeur 0. La deuxième ligne initialise la liste L contenant trois fois la variable R. Comme R désigne une association à la liste [0,0,0], les éléments de la liste L représentent trois associations à la liste R :

images/05RI23.png

En effectuant L[0][1] = 5, nous recherchons en premier la signification de L[0]. Comme L[0] désigne la liste R, cette écriture équivaut à : R[1] = 5.

images/05RI24.png

À ce niveau, en utilisant l’instruction print(L), nous obtenons :

>> [[0, 5, 0], [0, 5, 0], [0, 5, 0]] 

L’affichage de la variable L donne son contenu, c’est-à-dire que toutes les associations sont remplacées pour n’afficher que les valeurs. Nous avons l’impression que finalement L correspond à une liste de trois listes de trois éléments. Cela est vrai, mais faux aussi. Car si nous affichons :

print(id(L[0])) 
>> 77187232 
print(id(L[1])) 
>> 77187232 
print(id(L[2])) 
>> 77187232 

nous constatons que ces trois listes sont en fait la même et unique liste nommée R dans l’exercice.

La création de la liste de listes S utilise cette fois-ci trois listes indépendantes :

images/05RI25.png

Ainsi, en effectuant S[0][1] = 5, on modifie la case centrale de la liste du haut :

images/05RI26.png

À ce niveau, en appelant la fonction print(S), nous obtenons :

>> [[0, 5, 0], [0, 0, 0], [0, 0, 0]] 

3. Création de liste de listes II

Donnez l’affichage en sortie du programme suivant :

T = [ 1 , 2 , 3] 
L=[] 
L.append(T) 
L.append(T) 
T[1] = 9 
print(L) 

Correction guidée :

Tout d’abord, nous initialisons la liste T avec trois valeurs. Ensuite, nous créons une liste vide L dans laquelle nous insérons...

Préparation aux exercices

Afin de faire des exercices d’entraînement plus variés, nous allons vous présenter un concept nouveau.

1. Une fenêtre d’affichage graphique images/etoile.png

La correction de cet exercice est disponible en téléchargement depuis l’onglet Compléments. Pour agrémenter certains de nos exercices, nous allons faire des affichages graphiques. Pour cela, nous utilisons la librairie Tkinter gérant les applications fenêtrées. Cette librairie est installée par défaut avec Python.

Tous les programmes que nous avons faits jusqu’à maintenant étaient réalisés en mode console, c’est-à-dire que nous avions une interface textuelle : nous pouvions entrer des données au clavier et les affichages se faisaient sous format texte. Il existe une autre catégorie d’applications dans le monde informatique : les applications fenêtrées. Ces applications ouvrent une fenêtre sur le Bureau, comme Word, Photoshop, Chrome, Calculatrice… et elles affichent leur espace de travail à l’intérieur de cette fenêtre. On peut interagir avec la souris avec ces applications, ce qui n’était pas le cas des applications console. Mais surtout, on peut afficher des graphiques à l’écran.

Voici un noyau pour une application faisant...

Exercices d’entraînement

1. Calculer le minimum d’une liste images/etoile.png

La correction de cet exercice est disponible en téléchargement depuis l’onglet Compléments. Écrivez une fonction qui reçoit une liste de taille quelconque et qui retourne le plus petit nombre contenu dans cette liste. Testez votre programme sur un exemple. On suppose que la liste n’est pas vide. Voici quelques conseils :

  • Il faut utiliser une boucle for pour parcourir les éléments de la liste.

  • Il existe un piège non évident dans cet exercice. En effet, la logique de la boucle principale est simple : on parcourt chaque élément, si l’élément courant est plus petit que le minimum connu, on remplace le minimum connu par la nouvelle valeur. Cependant, il reste un mystère à résoudre : à quelle valeur doit-on initialiser la variable correspondant au minimum courant ? En fait, vu le test effectué, il faudrait une valeur très grande, donc 1 000 par exemple. Mais si notre liste contient que des nombres supérieurs à 1 000, le résultat sera faux. Alors on peut mettre un nombre encore plus grand comme 1 000 000.

    Oui, mais non, car il suffit que tous les nombres dans la liste soient supérieurs à cette valeur et on aura un problème. Alors, on peut mettre + ! Déjà c’est une meilleure idée. Plus simplement, nous vous proposons une autre option où, pour initialiser la valeur du minimum courant, nous allons prendre la valeur du premier élément de la liste.

2. Dessiner en mode texte images/etoile.png

La correction de cet exercice est disponible en téléchargement depuis l’onglet Compléments. Nous vous proposons de réaliser des fonctions de dessin en mode texte. Vous allez vous servir de la console comme zone d’affichage. Voici quelques conseils :

  • Créez une liste de listes de dimension 31x31 remplies avec le caractère point ".". Vous pouvez utiliser la syntaxe suivante : ["."]*31 pour créer trente et un éléments rapidement.

  • Créez une fonction Affichage() pour afficher la grille de dessin ligne par ligne. Chaque caractère sera affiché deux fois dans le sens horizontal pour corriger les proportions, car un caractère est deux fois plus haut que large. Utilisez le paramètre end="" de la fonction print pour éviter un retour à la ligne.

  • Créez une fonction SetPixel(x,y,carac) qui met le caractère passé en paramètre dans la case (x,y) de la liste de listes. Testez d’abord si les coordonnées x et y sont dans la plage 0 à 30, sinon ne faites rien.

  • Créez une fonction LigneHoriz(x1, y, x2, carac) qui dessine une ligne horizontale allant de la case (x1,y) à la case (x2,y) et qui remplit chaque case avec le caractère passé en paramètre. Cette fonction doit utiliser la fonction SetPixel().

  • Créez une fonction LigneVert(x,y1,y2,carac) dessinant une ligne verticale allant de la case (x,y1) à la case (x,y2) afin que chaque case soit remplie avec le caractère passé en paramètre. Cette fonction doit utiliser la fonction SetPixel().

  • Créez...

Projets

Les projets de ce chapitre sont très intéressants mais aussi assez longs, c’est-à-dire entre 80 et 100 lignes chacun. Pour les rendre abordables, nous les avons découpés en étapes de difficulté croissante. Chaque étape fait donc une trentaine de lignes, ce qui rend l’objectif réalisable. À chaque fin d’étape, nous donnons une correction pour vous permettre de trouver les solutions des points qui vous ont posé problème ou pour voir une approche différente de la vôtre.

1. Le jeu du morpion - Étape 1 images/etoile.png

Le jeu du morpion se joue sur une grille 3x3 à deux joueurs. Un joueur fait des cercles et l’autre des croix dans les cases, à tour de rôle. Celui qui réussit à faire un alignement vertical, horizontal ou diagonal a gagné. Nous allons décrire comment construire ce jeu en trois étapes.

Étape 1 : Création de la grille de jeu

La correction de cette première partie du projet est disponible en téléchargement depuis l’onglet Compléments.

  • Utilisez le modèle habituel pour créer la fenêtre Tkinter, elle doit faire 300 pixels de large :

# MORPION 
from tkinter import Tk, Canvas 
import random 
 
TAILLE = 300 
 
... 
 
# Création de la fenêtre de dessin 
Mafenetre = Tk() 
Mafenetre.geometry(str(TAILLE) +"x"+str(TAILLE)) 
canvas = Canvas(Mafenetre,width=TAILLE, height=TAILLE,  
borderwidth=0, highlightthickness=0,bg="lightgray") 
canvas.pack() 
Mafenetre.after(100,PROG) 
Mafenetre.mainloop() 
  • Créez une liste de listes pour modéliser la grille. Initialisez ses valeurs à 0 pour la première ligne, 1 pour la deuxième et 2 pour la dernière.

  • Écrivez une fonction AfficheGrille() (c="black") qui dessine deux lignes verticales et deux lignes horizontales pour créer les 9 cases du jeu. Utilisez la fonction canvas.create_line((x1,y1),(x2,y2)) pour dessiner un segment entre deux points. Le paramètre width doit être fixé à 3 pour que les segments soient épais. Le paramètre fill permet de choisir la couleur du trait, faites en sorte que la couleur soit donnée par le paramètre c de la fonction AfficheGrille().

  • Créez une fonction AffichePions() qui remplit la grille suivant les coups joués. Pour cela, utilisez deux boucles for avec indices pour parcourir la grille. Si la valeur associée au pion vaut 1, dessinez un rond bleu, si la valeur vaut 2, dessinez une croix rouge avec deux segments d’épaisseur 10 pixels. Idéalement, la croix et le rond doivent être un peu plus petits que la case.

  • Dans la fonction PROG(), appelez la fonction AfficheGrille() et AffichePions()

Pour valider cette première étape, vous devez obtenir le résultat suivant :

images/05RI68.png

Correction étape 1 :

# MORPION - Étape 1 
from tkinter import Tk, Canvas 
import random 
 
TAILLE = 300 
JEU = [ [0,1,2],[0,1,2],[0,1,2]] 
 
def AfficheGrille(c="black"): 
  canvas.create_line((100,0),(100,300),width=3,fill=c) 
  canvas.create_line((200,0),(200,300),width=3,fill=c) ...