1.1 Toujours une minuscule initiale
Vous remarquerez que tous les noms utilisés sur mes variables commencent par une minuscule. Respectez cette habitude. Un nom qui commence par une majuscule, c'est pour autre chose qu'une variable.
Nous allons en voir un peu plus sur les variables et un peu sur le module random qui permet de gérer l'aléatoire.
Cette image n'était qu'une première approche. Nous allons en apprendre plus sur la façon dont Python gère ces mystérieuses boîtes.
Enfin, nous verrons pourquoi Sauron L'Oeil Qui Voit Tout n'est pas le seul à pouvoir être développeur, même sur un projet de 300 000 lignes.
Logiciel nécessaire pour l'activité : Thonny
Evaluation : -
✎ questions 09
✌ -
Exercices supplémentaires 🏠 : Exercices
Documents de cours PDF : .PDF
Sources latex : .TEX et entete.tex et licence.tex
Résumé : Version HTML ou fond blanc ou ou PDF (couleur ou gris)
Vous remarquerez que tous les noms utilisés sur mes variables commencent par une minuscule. Respectez cette habitude. Un nom qui commence par une majuscule, c'est pour autre chose qu'une variable.
L'espace ne peut pas faire partie des noms de variables pour la simple et bonne raison qu'il joue le rôle de caractères délimitateurs. Par contre, Python est sensible à la casse : il distingue les minuscules et les majuscules. Les variables toto et toTO désignent deux variables différentes.
Voici deux techniques usuelles pour les longs noms de variables.
Donnez à vos variables des noms explicites de plus d'une lettre : des noms permettant de savoir au premier coup d'oeil ce qu'elles contiennent. a, b, c, ce n'est pas forcément explicite.
N'utilisez jamais de variable dont le nom est l (L minuscule) : il est trop difficile à distinguer d'un I (I majuscule) !
Exemple : si une variable contient le nombre de paquets de chips à acheter, on pourrait prendre :
Vous avez déjà vu que certains noms ne doivent pas être donnés à vos programme : turtle.py, math.py...
De la même façon, il existe des noms réservés qu'on ne peut pas utiliser comme nom pour une variable car l'interpréteur Python les utilise déjà comme mots-clés :
False class finally is return
None continue for lambda try
True def from nonlocal while
and del global not with
as elif if or yield
assert else import pass
break except in raise
Python accepte n'importe quel caractère dans les noms de variables (à part l'espace !).
Néanmoins, n'utilisez que les caractères non accentués car certains langages de programmation ne permettent pas de placer de tels caractères.
D'ailleurs, vos variables, vos fonctions et vos programmes seront en anglais si vous continuez en informatique dans le supérieur. Pas d'accent donc.
Rappels des épisodes précédents :
Vous pouvez les relire en diagonale si vous ne vous souvenez plus ce qu'on y raconte :
L'affectation est le nom qu'on donne au mécanisme permettant de créer une liaison entre un NOM et un CONTENU MEMOIRE.
1 |
|
L'opérateur = permet de créer une variable nommée a et y stocker la valeur 10.
On peut représenter naïvement ce mécanisme comme une boite qui porte un NOM et dans laquelle on place le CONTENU associé.
Néanmoins, une affectation est simplement un moyen de faire la liaison entre un NOM et un CONTENU en mémoire :
Notez bien que c'est le CONTENU 10 qu'on associe au NOM a : on va de la droite vers la gauche. C'est pour cela qu'on écrit plutôt ceci dans les algorithmes :
a ← 10
Pour obtenir le contenu d'une variable, il suffit
>>> a = 100
>>> a
100
1
2 |
|
Incrémenter de 1 en partant de 45 veut dire qu'on passe à 46.
Incrémenter de 2 en partant de 45 veut dire qu'on passe à 47.
Incrémenter de 5 en partant de 45 veut dire qu'on passe à 50.
Incrémenter de -1 en partant de 45 veut dire qu'on passe à 44.
On dit plutôt décrémenter de 1 d'ailleurs.
Conclusion
Incrémenter veut dire utiliser la valeur actuelle d'une variable pour calculer sa nouvelle valeur.
Incrémenter (sans autre information donnée) veut dire qu'on incrémente de +1. il s'agit de la valeur par défaut.
1
2 |
|
La demande s'effectue bien de la droite vers la gauche :
Vous savez maintenant EXECUTER MENTALEMENT le programme ci-dessous : il va afficher une valeur de 15.
1
2
3
4
5 |
|
Oui, mais : on ne vous demande pas d'EXECUTER le programme : c'est le travail de l'ordinateur.
En tant qu'humain, vous avez juste besoin que de COMPRENDRE ce que réalise le programme.
J'ai remplacé les contenus réels des variables par ... : vous devriez comprendre ce que le programme va réaliser, un jour, lorsqu'il aura accès à des valeurs précises.
1
2
3
4
5 |
|
L'utilisation d'une variable permet de COMPRENDRE un programme sans savoir EXACTEMENT ce que seront les données. Par contre, vous avez besoin de connaître le type d'informations qu'elles contiendront.
✔ 01-A° Dans Thonny, sélectionner le menu AFFICHAGE-VIEW et y prendre l'option VARIABLES. Vous devriez voir un panneau s'afficher sur la droite.
✔ 01-B° Lancer le programme.
1
2
3
4
5
6 |
|
Vous devriez obtenir ceci :
Questions
Les variables créées ont-elles le bon contenu ?
Pourquoi la ligne 6 n'a-t-elle pas provoqué d'affichage ?
✔ 01-C° Remplacer le programme par celui-ci qui diffère juste sur la ligne 5. On utilise la fonction print() vue dans Python 01 : elle permet de demander à Python d'afficher dans la console.
1
2
3
4
5 |
|
Vous devriez obtenir ceci :
Conclusion : dans un programme, toute demande d'affichage doit être faite de façon explicite : avec print().
Fin de la zone des rappels.
Nouveautés :
Une expression est une séquence de valeurs associées à des opérateurs.
L'interpréteur peut évaluer l'expression pour en fournir la valeur.
Une expression ne modifie pas l'état du programme car elle fournit une valeur sans la stocker définitivement.
Dans un programme : une ligne ne comportant qu'une expression n'a aucun effet externe :
1 |
|
Dans la console interactive : une ligne ne comportant qu'une expression affiche la valeur après interprétation de cette expression :
>>> "bon" * 2
'bonbon'
Une instruction modifie l'état du programme (cela veut dire qu'on modifie les données stockées).
Toutes les affectations sont donc des instructions puisqu'elles modifient les variables en mémoire.
Par contre, certaines instructions ne sont pas des affectations.
L'utilisation de la méthode forward() du module Turtle est bien une instruction : on modifie les zones mémoires contenant les coordonnées de la tortue.
>>> "bon" * 2 # EXPRESSION (affichage car console+expression seule)
'bonbon'
>>> a = "bon" * 2 # INSTRUCTION : AFFECTATION
>>> print("bon" * 2) # INSTRUCTION (on agit sur l'IHM Console)
bonbon
Remarque : print() modifie certaines zones mémoires même si vous n'y avez pas accès directement : en réalité, le string "bonbon" est enregistré dans un fichier puis on vide ce fichier pour provoquer l'affichage. Il y a donc bien modification de la mémoire.
⚙ 02-A° Utiliser ce programme et répondre à ces questions :
1
2
3
4
5 |
|
...CORRECTION...
Vous devriez obtenir ceci :
On voit qu'on affiche deux fois 20 : la ligne 4 n'a pas modifié a. On a juste demandé à Python de calculer ce que fait a + 100 sans lui demander ni de l'afficher, ni de le stocker.
1
2
3
4
5 |
|
⚙ 02-B° Utiliser ce programme et répondre à ces questions :
1
2
3
4
5 |
|
...CORRECTION...
Vous devriez obtenir ceci :
Cette fois, nous sommes bien parvenus à modifier la variable.
1
2
3
4
5 |
|
⚙ 03° Sans lancer le programme, quel sera l'affichage obtenu et les variables finales des trois variables ? Soyez rigoureux dans votre réflexion.
1
2
3
4
5
6
7
8
9 |
|
...CORRECTION...
De façon séquentielle, on obtient les choses suivantes :
1
2
3
4
5
6
7
8
9 |
|
⚙ 04° Sans lancer le programme, quel sera l'affichage obtenu et les variables finales des trois variables ? Soyez rigoureux dans votre réflexion.
1
2
3
4
5
6
7
8 |
|
...CORRECTION...
1
2
3
4
5
6
7
8 |
|
⚙ 05° Passons en mode console. Expliquer, en la justifiant, la valeur finale qui sera attribuée à b sur la dernière ligne. Pourquoi l'expression b affiche-t-elle quelque chose ici ?
>>> b = 10
>>> b = b + 1
>>> b = b * 2
>>> b
...
...CORRECTION...
>>> b = 10 # b référence 10
>>> b = b + 1 # incrémentation de 1, donc 11
>>> b = b * 2 # multiplication par 2, donc 22
>>> b # Simple évaluation dans la console, donc provoque un affichage
22
⚙ 06° Expliquer les deux affichages obtenus :
>>> a = 5
>>> type(a)
...
>>> a*2
...
>>> b = str(a)
>>> type(b)
...
>>> b*2
...
...CORRECTION...
1e cas : a désigne 5, un integer.
L'opérateur * appliqué sur a revient donc à faire une multiplication et donne bien 10.
b est le transtypage en string de 5 : "5".
L'opérateur * appliqué sur b revient donc à faire une répétition : b * 2 est alors évaluée à "55"
⚙ 07° Expliquer la valeur finale qui sera attribuée à b sur la dernière ligne sur les deux exemples ci-dessous.
Cas 1
>>> a = "10"
>>> b = 2 * a
>>> b
...
Cas 2
>>> a = 10
>>> b = a
>>> b = b + 1
>>> b
...
...CORRECTION...
Cas 1
>>> a = "10" # a référence un STRING !
>>> b = 2 * a # b référence donc "1010"
>>> b # simple évaluation, donc affichage
'1010'
Cas 2
>>> a = 10 # a référence un integer
>>> b = a # b référence donc 10
>>> b = b + 1 # Incrémentation, on passe à 11
>>> b # Simple évaluation
11
Si une variable est définie à un certain moment à partir du contenu d'une autre variable, la modification ultérieure de cette variable n'aura aucune conséquence.
>>> a = 10
>>> b = a # on définit donc b à 10 ici
>>> a = 20
>>> b # avoir modifié a ne remodifie pas b !
10
⚙ 08° Expliquer, en la justifiant, la valeur finale qui sera attribuée à b sur la dernière ligne.
>>> a = 10
>>> b = 2 * a
>>> a = 0
>>> b
...
...CORRECTION...
>>> a = 10 # a référence 10
>>> b = 2 * a # b référence 20
>>> a = 0 # a référence maintenant 0
>>> b # simple évaluation
20
Le point important est bien évidemment qu'on n'a pas recalculé b sous pretexte que a a été modifiée après l'affectation de b.
✎ 09° Sans taper les lignes de commandes, tenter de trouver la valeur finale qui sera attribuée à b sur la dernière ligne. Justifier votre avis. Vérifier.
>>> a = "Pa"
>>> b = "n "
>>> b = a + b
>>> b = b * 3
>>> a = "Ro"
>>> b
...
...CORRECTION...
>>> a = "Pa"
>>> b = "n "
>>> b = a + b # concaténation "Pan" dans b
>>> b = b * 3 # Répétition "PanPanPan" dans b
>>> a = "Ro" # Modification de a
>>> b # Simple évaluation mais en mode console
'PanPanPan'
Encore une fois, pas de retro-action.
Une chose très courante en informatique : la permutation de deux contenus, c'est à dire l'"inversion".
Puisqu'un humain a deux mains, il est facile pour lui de permuter deux objets de place.
L'ordinateur ne peut faire qu'une chose à la fois.
Il aura besoin d'une zone de stockage temporaire.
Pour permuter les contenus de argentGauche et argentDroite, il faut une troisième variable videPoche temporaire.
Cela se fait en 3 étapes :
--> on copie le contenu de droite dans videPoche.
--> on écrase la valeur à droite en y plaçant la valeur à gauche. Sans videPoche, l'ancienne valeur de argentDroite aurait disparue.
--> on écrase la valeur à gauche en y plaçant le contenu de la variable temporaire qui contient la valeur initialement à droite
>>> g = 7
>>> d = 55
>>> temp = d
>>> d = g
>>> g = temp
>>> g
55
>>> d
7
Le but de la NSI est de comprendre comment les choses fonctionnent, pas d'appliquer des recettes magiques. Dans un premier temps, on vous demandera donc d'appliquer plutôt la méthode en 3 lignes. Mais Python permet de permuter en une ligne :
g, d = d, g
>>> g = 7
>>> d = 55
>>> g, d = d, g
>>> g
55
>>> d
7
En 1er NSI, vous aurez à utiliser la méthode en 3 lignes. Une fois que cela sera acquis, vous aurez le droit à la permutation en une ligne en Terminale.
⚙ 10° Compléter le programme pour permettre la permutation des variables x et y en utilisant une variable temporaire intermédiaire.
1
2
3
4
5
6
7
8
9
10
11
12
13
14 |
|
...CORRECTION...
1
2
3
4
5
6
7
8
9
10
11
12
13
14 |
|
⚙ 11° Compléter le programme pour permettre la permutation des variables x et y en une seule ligne.
1
2
3
4
5
6
7
8
9
10
11
12 |
|
...CORRECTION...
1
2
3
4
5
6
7
8
9
10
11
12 |
|
Une variable est un symbole qui associe un nom et une valeur.
L'association est variable au cours du temps.
Exemple
Sur l'exemple, la variable a commence par référencer 10 puis 20 un peu plus loin dans le programme.
1
.
.
28
|
|
Une CONSTANTE est un symbole qui associe DEFINITEMENT un nom et une valeur.
Par convention, les noms des constantes sont constitués uniquement de majuscules.
Bien entendu, on peut changer la valeur entre chaque lancement de programme, mais on ne doit pas la modifier en utilisant une deuxième affectation ailleurs dans le programme.
Exemple
1
.
. |
|
on ne doit jamais modifier le contenu de NBR_CASES à moins de relancer le programme avec une autre valeur.
Il n'y a aucun moyen de créer de véritables constantes en Python. Un nom de variable en majuscule informe le développeur qu'on ne veut pas modifier cette variable après la première affectation.
Un autre facilitateur que nous éviterons d'utiliser mais que vous pourriez rencontrer :
>>> a = b = c = 10
>>> a
10
>>> b
10
>>> c
10
Nouveau sujet aujourd"hui : comment noter explicitement dans le code le type de ce que devrait référencer une variable ?
Il n'est pas toujours évident qu'une variable référence un int ou un str ou un list. Voyons comment l'indiquer facilement et clairement.
La fonction input() permet de récupérer sous forme d'un STRING ce qu'un utilisateur va taper un jour sur le clavier.
L'utilisateur doit valider sa réponse avec la touche ENTREE.
1
2
3
4 |
|
Pour comprendre le programme ci-dessus, il vous suffit de savoir que la variable reponse va contenir un string correspondant aux touches enfoncées par l'utilisateur.
Réponse simple : on ne peut pas ! Tout ce que vous tapez va être transmis à Python sous forme d'un string.
Si vous tapez 17, input() renvoie un string "17", et pas un int 17.
Mais; on peut utiliser la fonction native int() qui permet de tenter de créer un entier à partir de la donnée qu'on lui fournit. Exemple d'utilisation :
1
2
3
4 |
|
En Python, il est possible (pas obligatoire) d'indiquer, autrement qu'avec un commentaire #, le type de telle ou telle variable.
Lorsqu'on déclare une variable, on peut intercaler le type avant de placer l'opérateur d'affectation =.
1 |
|
Sur ce premier exemple, on voit bien que l'indication ne sert à rien : on affecte directement à droite un string bien visible.
Par contre, ceci est un peu plus utile :
1 |
|
1
2
3
4
5
6
7 |
|
Sans utilisation d'un module supplémentaire, cela n'est qu'une simple indication, un commentaire pour la personne qui va lire votre code. Nous pourrions faire la même chose en tapant ceci :
1
2
3
4
5
6
7 |
|
En Python, cette façon d'indiquer les types n'est pas souvent utilisés car elle n'était pas présente dans les premières versions et la plupart du temps le code lui-même est déjà explicite quand à la nature des données référencées.
Par contre, elle est beaucoup plus présente sur les paramètres des fonctions.
⚙ 12° Remplacer les ... du programme pour
On ne tiendra pas compte de la date d'anniversaire, juste de l'année de naissance.
1
2
3
4
5
6
7 |
|
...CORRECTION...
1
2
3
4
5
6
7 |
|
Passons à une documentation vraiment utilisée couramment : la documentation rapide des fonctions.
1
2
3 |
|
Nous avions vu la signature d'un opérateur (comme +) : int + int -> int
Nous allons voir le même principe pour les fonctions.
Arguments | ⇒ | Fonction | ⇒ | Réponse |
Pour pouvoir utiliser une fonction, on a besoin :
Toute ces informations se retrouvent dans la signature. Par exemple, la signature d'une fonction addition() qui permet d'additionner deux entiers :
addition(int, int) -> int
Prototyper une fonction consiste à rajouter les noms des paramètres à la signature.
addition(nombre1:int, nombre2:int) -> int
Attention : le prototype ne donne pas d'indications sur ce que réalise la fonction. On peut s'en douter en regardant les noms de la fonction et des paramètres si ils sont explicites, mais on ne fait que deviner. On peut donc se tromper.
Spécifier une fonction consiste à donner :
Sous la première ligne du prototype, on rajoute donc un string explicatif :
Exemple
Voilà la spécification de notre fonction-exemple :
1
2
3 |
|
La fonction help() renvoie les informations fournies dans la documentation.
>>> help(addition)
Help on function addition in module __main__:
addition(nbr1: int, nbr2: int) -> int
Renvoie la somme de nbr1 et nbr2
⚙ 13° Modifier la fonction calculer_moyenne() pour qu'elle renvoie la réponse attendue : elle doit renvoyer la moyenne des deux notes reçues. Pour l'instant, elle renvoie 0 quoi qu'on lui envoie.
1
2
3
4
5
6
7
8 | def calculer_moyenne(note1:int, note2:int) -> float:
"""Renvoie la moyenne des deux notes envoyées"""
return 0.0
ma = calculer_moyenne(10, 20)
mb = calculer_moyenne(10, 15)
print(ma)
print(mb)
|
...CORRECTION...
1
2
3
4
5
6
7
8 | def calculer_moyenne(note1:int, note2:int) -> float:
"""Renvoie la moyenne des deux notes envoyées"""
return (note1 + note2) / 2
ma = calculer_moyenne(10, 20)
mb = calculer_moyenne(10, 15)
print(ma)
print(mb)
|
Cette dernière partie est destinée à être commentée par l'enseignant au tableau plutôt que d'être réalisée en autonomie.
Python est un langage utilisant un typage dynamique : le type d'une variable est décidé par l'intepréteur au moment de son affectation. D'ailleurs en fonction des moments dans le programme, un nom de variable peut faire référence à des types différents. Exemple :
1
2
3
4
5 |
|
Avec ce programme, on obtient à l'affichage 127.19999999999999.
Pourquoi ? Tout simplement à cause de la signature int * float -> float
Le typage dynamique est sympathique lorsqu'on commence à programmer mais en réalité, on peut réaliser assez facilement des erreurs de sémantique : Python peut réaliser des choses différentes de ce que vous aviez en tête en lui fournissant une ligne de code.
D'autres langages, comme le C, adopte le typage statique : le développeur doit choisir définitivement le type associé à sa variable. Avant de faire l'affectation, il faut donc déclarer la variable, c'est à dire fournir son type associé. Exemple :
1
2
3
4
5
6
7
8
9 |
|
On notera d'abord qu'en C, on doit créer une première fonction qui sera appelée automatiquement lors du lancement : la fonction main(), comme principal. C'est ainsi qu'on sait où se trouvent les instructions du programme principal.
Observez bien sur les lignes 3-4-5, on donne le type de retour de la fonction et le type des variables lors de leur déclaration.
Le typage statique indique que la variable a est un int, lors du calcul, C effectue donc cette conversion sur le caclul et le résultat affiché est 127 : un integer.
C'est l'un des problèmes du typage statique : beaucoup de conversions peuvent être réalisées sans qu'on s'en rende compte.
D'autres langages, comme Ocaml, ne nécessite pas nécessairement d'indiquer le type des variables mais c'est le langage lui-même qui infère (déduit) des différents types en fonction des lignes qu'il analyse. D'ailleurs, s'il ne parvient pas à trouver de cohérence de type sur une variable, il n'acceptera pas le code. Exemple : on reprend notre exemple et vous allez voir qu'Ocaml possède un opérateur de multiplication sur les integers (*) et un opérateur de multiplication sur les flottants (*.). On ne parvient même pas à réaliser notre multiplication.
1er tentative : avec *
1
2
3
4
5
6 |
|
Si on lance, on obtient ce refus :
File "typage.ml", line 4, characters 12-13:
4 | let a = a * b
^
Error: This expression has type float but an expression was expected of type int
Ocaml refuse de commencer car il a inféré que a est un int, b un float et on tente d'utiliser l'opérateur * dont la signature UNIQUE est signature int * int -> int.
2e tentative : avec *.
1
2
3
4
5
6 |
|
Si on lance, on obtient ce refus :
File "typage.ml", line 4, characters 8-9:
4 | let a = a *. b
^
Error: This expression has type int but an expression was expected of type float
Ocaml refuse de commencer car il a inféré que a est un int, b un float et on tente d'utiliser l'opérateur *. dont la signature UNIQUE est signature float * float -> float.
3e tentative : convertir le contenu de a en float
1
2
3
4
5
6 |
|
Cette fois, tout fonctionne et Ocaml affiche 127.200000
Cette partie est destinée à être commentée par l'enseignant au tableau plutôt que d'être réalisée en autonomie.
Vous allez voir maintenant pourquoi il est inutile de donner des noms différents aux paramètres de plusieurs fonctions. Sur l'exemple ci-dessous, Python ne fera aucune confusion entre les trois versions du paramètre feutre :
1
2
3 |
|
Heureusement car sinon, il faudrait à chaque fois donner un nom un peu différent comme sur cet exemple :
1
2
3 |
|
Vous imaginez si on devait se souvenir des noms de toutes les variables et paramètres utilisés dans les 300 000 lignes de votre programme... Il n'y aurait qu'un seul informaticien au monde et il ressemblerait à l'Oeil Qui Voit Tout et travaillerait depuis le Mordor...
On appelle variable locale une variable temporaire qui est définie dans une fonction.
Conséquences :
1
2
3
4
5
6
7
8
9 |
|
Ligne 6 (avant l'appel) lire a depuis le programme principal ne fonctionne pas. On provoquerait une erreur puisque a n'existe pas.
Ligne 8 (après l'appel f(10)) : lire a depuis le programme principal provoque une erreur puisque a n'existe plus.
print(a) # on affiche la variable locale
NameError: name 'a' is not defined
Ligne 4 (pendant l'un des appels) : lire a depuis la fonction fonctionnerait (mais il faut supprimer ou commenter les lignes 6 et 8 avec # pour les empêcher de s'exécuter).
Depuis la fonction :
20
Depuis la fonction :
60
On appelle variable globale une variable permanente qui est définie dans le corps du programme, hors de toute fonction.
Permanente, elle existe jusqu'à la fin du programme.
Seule possibilité pour qu'elle disparaisse : la supprimer volontairement avec le mot-clé del. Exemple : del a permet de supprimer la variable a.
En ligne 2, on lit directement valeur globale depuis la fonction.
1
2
3
4
5
6
7
8
9 |
|
Depuis la fonction :
20
Depuis la fonction :
60
Voici un autre programme montrant que la fonction ne parvient pas à modifier la variable globale : ligne 2, on crée en réalité une variable locale qui disparaitra après l'appel.
Il y aura donc temporairement deux variables portant le même nom. Depuis la fonction, on lira juste la variable locale, et on ne pourra plus voir la variable globale.
1
2
3
4
5
6
7
8
9
10
11 |
|
Avant l'appel à f() :
2
Pendant l'appel à f() :
100
Après l'appel à f() :
2
Il faut bien comprendre que faire une affectation en local en utilisant le même nom qu'une variable globale ne fait que créer temporairement une variable locale qui va cacher la variable globale.
Une fois sortie de la fonction, la variable globale n'aura pas bougé.
1
2
3
4
5
6
7
8
9 |
|
En lignes 1 à 5, a est locale à la fonction.
En lignes 7, 8 et 9, a est globale au programme.
On rajoute mentalement des suffixes aux variables qui portent le même nom !
Même programme mais en rajoutant des suffixes dans sa tête :
1
2
3
4
5
6
7
8
9 |
|
En lignes 1 à 5, la variable a_mod est une variable locale à la fonction.
En lignes 7, 8 et 9, la variable a_glo est une variable globale au programme. Il ne s'agit pas de la même que celle des lignes 1 et 5.
Par contre, au début de l'appel puisqu'on envoie a_glo dans a_loc, ce sont temporairement des alias qui pointent vers le même contenu en mémoire. Jusqu'à la ligne 3, où on réalise une affectation : on crée alors une vraie variable locale dissociée.
Visuel de la situation
Lors d'une affectation, Python crée une association entre le nom de la variable et un contenu en mémoire.
L'espace des noms correspond au mécanisme qui lie le nom de la variable à un contenu mémoire, représenté ici par le simple trait entre les deux.
Les associations entre nom et contenu sont mémorisées dans une table qu'on nomme l'espace des noms.
Imaginons qu'on dispose de ce programme :
1
2
3 |
|
Voici l'image simpliste qu'on peut se faire de la liaison entre l'espace des noms et l'espace mémoire :
Cette liaison est réalisée en associant dans une table chaque nom de variable à une zone mémoire.
En Python, on peut récupérer l'adresse / référence / identifiant à l'aide de la fonction native id().
>>> a = 10
>>> b = 20
>>> id(a)
108
>>> id(b)
524
Il existe en réalité plusieurs espaces des noms pouvant référencer l'espace mémoire :
On peut voir visuellement ces zones dans Python Tutor. La zone grise est l'espace des noms actuellement utilisé par Python.
Exemple avec ce court programme :
1
2
3
4
5 |
|
Lorsque Python doit évaluer une variable dans une fonction :
Traduit en Python, cela donnerait quelque chose comme ceci :
1
2
3
4
5
6
7
8
9
10
11
12
13 |
|
Dans d'autres langages (comme le C par exemple), l'adresse d'une variable ne change pas après déclaration. C'est bien le contenu de la case mémoire qui change.
⚙ 14° On travaille avec le programme ci-dessous.
1
2
3
4
5 |
|
...CORRECTION...
L1 (déclaration)
L4 - L5(appel) - L1(stockage du paramètre) - L2 - L5(retour) - Fin
Pour répondre sans confusion aux questions, il suffit de voir le programme de cette façon :
1
2
3
4
5 |
|
L1 : La fonction ne s'exécute pas automatique en ligne 1 : il s'agit juste d'une déclaration. On met la fonction modification() dans l'espace global des noms et à partir de maintenant, on pourra y faire appel.
L4 : la variable a_glo du programme principal contient visiblement 10.
L5 : On lance l'appel à la fonction modification() en lui transmettant a_glo qu'on stocke dans un paramètre local qui se nomme a_mod ! Lorsqu'on exécute la ligne 2, on incrémente de 5 la variable a_mod qui passe maintenant à 15.
Ensuite, on sort de la fonction qui répond... rien.
L'espace des noms de la fonction disparait alors, emportant avec lui notre variable a_mod.
La variable a_glo n'a donc pas été modifiée. Elle référence toujours 10.
>>> %Run portee.py
>>> a
10
Le module random contient beaucoup de fonctions permettant de gérer l'aléatoire.
Il existe notamment la fonction randint() qui permet de générer un integer aléatoire :
randint(5, 8) génère un nombre aléatoire entre 5 et 8 pouvant valoir 5, 6, 7 ou 8.
Si on veut simuler un d6, un dé à 6 faces, il suffit de taper randint(1, 6) (notez bien qu'on doit d'abord importer la fonction depuis le module avant de pouvoir l'utiliser) :
>>> from random import randint
>>> randint(1, 6)
4
>>> randint(1, 6)
1
>>> randint(1, 6)
6
>>> randint(1, 6)
2
Ci-dessous, un programme générant 10 notes aléatoires entre 0 et 20 (notez bien qu'ici, nous importons uniquement le module random) :
1
2
3
4
5
6
7 |
|
>>> %Run portee.py
[19, 14, 5, 7, 3, 18, 16, 1, 8, 7]
>>> %Run portee.py
[4, 7, 13, 13, 11, 16, 4, 7, 9, 12]
Notez bien :
A vous de le savoir, cela a été défini comme cela.
⚙ 15° Répondre aux questions suivantes liées aux appels de fonctions.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 |
|
...CORRECTION...
Ligne 4, une seule entrée à fournir un jour.
Ligne 17.
On voit ligne 17 qu'on envoie nombre, donc 5.
En regardant ligne 4, on voit qu'elle stocke son entrée dans nbr.
L1 (importation du module, pas de la fonction directement)
L4 (déclaration d'une fonction)
L16
L17 (appel de fonction) - L4 (réception de l'entrée 5)
L7 - L8
L9 (avec n=0)-L10-L11 (nous avons un range(6)
L9 (avec n=1)-L10-L11
L9 (avec n=2)-L10-L11
L9 (avec n=3)-L10-L11
L9 (avec n=4)-L10-L11
L9 (avec n=5)-L10-L11
L12-L13
L17 (retour au point d'appel) - L18
⚙ 16° Notez (sans justification particulière) pour chacune des variables surlignées dans le programme s'il s'agit d'une variable locale ou d'une variable globale. Dire ensuite ce que contient la mémoire à la fin du programme et qui restera accessible depuis la console : quelle(s) variable(s), quelle(s) fonction(s), quelle(s) module(s).
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 |
|
...CORRECTION...
1
2
3
4 (locale)
5
6
7(locale)
8(locale)
9(locale)
10(locale)
11
12
13
14
15
16(globale)
17
18 |
|
Après être sorti de la fonction, son espace des noms est supprimé.
Il ne restera donc en mémoire que :
⚙ 17° On a modifié l'endroit où on déclare les variables minimum et maximum.
Répondre aux questions suivantes :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 |
|
...CORRECTION...
1
2
3
4
5
6
7
8(globales)
9
10
11
12
13
14(globale)
15(globale)
16
17
18 |
|
Puisqu'il n'y a pas de déclaration de variables portant ce nom dans la fonction, il s'agit de variables globales.
Lorsqu'il tombe sur la ligne 8, l'interpréteur fouille d'abord dans l'espace des noms local. Il n'y trouve aucune variable portant ces noms. Il va alors fouiller dans l'espace des noms global et trouvera les deux variables globales.
Puisqu'une fonction peut LIRE les variables globales, il n'y aura aucune erreur. Le programme fonctionnera normalement et tirera des nombres aléatoires entre 20 et 100.
⚙ 18° On modifie le programme une dernière fois en remplaçant, sur la ligne 16, le nom de la variable globale nombre par n comme la variable de boucle de la ligne 7.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 |
|
...CORRECTION...
Aucun problème puisqu'il ne s'agit pas de la même variable !
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 |
|
Dès que la fonction a répondu, n_lan disparaît. Il n'y a donc aucune ambiguïté lorsqu'on demande à lire n. Il s'agit nécessairement de n_glo qui n'a jamais été modifiée et contient toujours son 5 du début.
Oui, on peut libérer la place mémoire attribuée à une variable. Pour cela, il faut utiliser le mot-clé del.
Exemple :
>>> a = 5
>>> a
5
>>> del a
>>> a
NameError: name 'a' is not defined
Effectivement, on peut également utiliser une autre notation.
1
2 |
|
Cela donne ici le même résultat que ceci :
1
2 |
|
Attention, les deux façons de faire sont équivalentes ici, mais pas toujours. Evitez ces notations pour l'instant. De toutes manières, elles ne seront pas utilisées dans les sujets de NSI. Gardez la méthode n°2. C'est plus long mais c'est moins compliqué à comprendre de toutes manières.
Activité publiée le 28 08 2019
Dernière modification : 02 07 2024
Auteur : ows. h.