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 donc la façon de stocker des informations pour les réutiliser plus tard.
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 -
✌ -
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 de variable car l'interpréteur Python les utilise déjà comme mots-clés lorsqu'il analyse le texte de votre script :
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 et vos fonctions devront porter des noms anglais si vous continuez en informatique dans le supérieur. Pas d'accent donc.
Rappels des épisodes précédents :
Les variables servent à stocker des données.
Le mécanisme d'affectation de variables consiste à 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 a ← 10 dans les algorithmes.
Pour obtenir le contenu d'une variable, il suffit de taper le nom de cette variable dans la console interactive. Nous verrons que cela n'est pas suffisant dans un programme.
Incrémenter veut dire modifier la valeur d'une variable en lui ajoutant une autre valeur.
Incrémenter de 1 veut dire qu'on augmente la valeur de 1 : 45 passe à 46.
Ce +1 est la valeur par défaut en l'absence d'information.
Incrémenter de 2 veut dire qu'on augmente la valeur de 2 : 45 passe à 47.
Incrémenter de -1 veut dire qu'on augmente la valeur de -1 : 45 passe à 44.
On dit plutôt décrémenter.
Pour incrémenter a de 1 : a = a + 1
1
2 |
|
En ligne 1, on associe 10 à a.
En ligne 2, on évalue a + 1, ce qui donne 11. On place ENSUITE ce résultat de 11 dans a : au final, a contient donc 11.
Nouveautés :
Une expression est un ensemble 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.. Elle fournit simplement une valeur, sans la stocker définitivement.
Dans un programme :
1 |
|
Ce programme n'a aucun effet, ni en mémoire, ni à l'affichage. Sa ligne est bien simplement une expression.
Dans la console interactive :
>>> "bon" * 2
'bonbon'
Attention, l'affichage obtenu est juste dû au fait qu'on soit dans la console : puisqu'on ne fait rien du résultat de l'expression, il est redirigé vers la sortie console. La même ligne n'aurait aucun effet dans un programme.
Une instruction modifie l'état du programme (soit sa mémoire, soit en modifiant l'IHM).
Dans un programme :
1 |
|
Dans la console :
>>> "bon" * 2 # EXPRESSION (comme elle est seule et qu'on est dans la console, on obtient son affichage)
'bonbon'
>>> a = "bon" * 2 # INSTRUCTION : AFFECTATION
>>> print("bon" * 2) # INSTRUCTION (on agit sur l'IHM Console)
bonbon
Notez bien que la console Python affiche le résultat d'une expression non redirigée ailleurs.
01 ✔° Dans Thonny, sélectionner le menu AFFICHAGE-VIEW et y prendre l'option VARIABLES. Vous devriez voir un panneau s'afficher sur la droite.
02° Taper les instructions suivantes dans la console de Thonny.
Questions
>>> a = 5
>>> a + 2
7
>>> b = a + 4
...CORRECTION...
>>> a = 5 AFFECTATION
>>> a + 2 SIMPLE EVALUATION
7
>>> b = a + 4 AFFECTATION
On demande simplement d'évaluer le résultat. La console Python est configuré pour afficher le résultat d'une évaluation si on ne rien du résultat évalué.
On voit apparaître deux variables nommées a et b. Elles référencent les résultats qu'on a calculé du côté droite.
Il n'y a pas d'affectation. Uniquement une évaluation.
03° Sans taper les lignes de commandes, estimer les deux résultats qui seraient affichés (ou pas !) à l'écran. Soyez vigilant !
Vérifier et demandez conseil si le résultat vous surprend.
>>> a = 5
>>> b = 10
>>> a = 2
>>> a + 2*b
...
>>> c = (a+2) * b
...
>>> c*2
...
...CORRECTION...
De façon séquentielle, on obtient les choses suivantes :
04° Que vaut a après avoir tout exécuté ? Comment se nomment ces affectations où on modifie le contenu d'une variable en fonction de son ancien contenu ?
>>> a = 20
>>> a = a + 1
>>> a
...
>>> a = a + 1
>>> a
...
>>> a = a + 2
>>> a
...
...CORRECTION...
>>> a = 20
>>> a = a + 1
>>> a
21
>>> a = a + 1
>>> a
22
>>> a = a + 2
>>> a
24
On part de 20 et on incrémente deux fois de 1 et une fois de 2. On atteint donc 24.
Ce sont des INCREMENTATIONS.
05° Expliquer, en la justifiant, la valeur finale qui sera attribuée à b sur la dernière ligne.
>>> 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 désigne elle la version 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 affectation
'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 donc besoin d'une zone de stockage temporaire.
Pour permeture 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
Dans les premiers mois de NSI, vous aurez d'abord à utiliser la méthode en 3 lignes. Une fois que cela sera acquis, vous aurez le droit à la permutation en une ligne (et nous verrons comment cela fonctionne).
10° Réaliser la permutation des variables x et y en utilisant une variable temporaire intermédiaire.
>>> x = "Alice"
>>> y = "Bob"
...CORRECTION...
>>> x = "Alice"
>>> y = "Bob"
>>> temp = x
>>> x = y
>>> y = temp
>>> x
'Bob'
>>> y
'Alice'
11° Réaliser la permutation des variables x et y en une seule ligne.
>>> x = "Alice"
>>> y = "Bob"
...CORRECTION...
>>> x = "Alice"
>>> y = "Bob"
>>> x,y = y,x
>>> x
'Bob'
>>> y
'Alice'
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
Cette partie est plutôt destinée à être commentée par le prof au tableau plutôt que d'être réalisée en total 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'ensemble de ces associations se nomme l'espace des noms.
Il existe en réalité plusieurs espaces des noms.
On peut voir visuellement ces zones dans Python Tutor. La zone grise est l'espace des noms actuellement utilisé par Python.
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 |
|
Cette fonction renvoie un dictionnaire qui contient plusieurs clés.
Cette fonction renvoie un dictionnaire qui contient plusieurs clés.
1
2
3
4
5
6
7
8
9
10
11
12
13 |
|
Imaginons qu'on nomme ce programme portee.py :
>>> %Run portee.py
Le dictionnaire de l'espace des noms local de f() :
{ 'x': 2,
'y': [10, 100, 1000, 10, 100, 1000]
}
Le dictionnaire de l'espace des noms global :
{ '__name__': '__main__',
'__doc__': None,
'__package__': None,
'__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x7f0bf3e6fcf8>,
'__spec__': None,
'__annotations__': {},
'__builtins__': <module 'builtins' (built-in)>,
'__file__': 'portee.py',
'f': <function f at 0x7f0bf3159c80>,
'a': 10,
'b': [10, 100, 1000],
'c': [10, 100, 1000, 10, 100, 1000]
}
>>> a
10
>>> globals()['a']
10
>>> locals()['y']
KeyError: 'y'
12° Sans lancer le programme ci-dessous, répondre aux questions suivantes en utilisant juste votre compréhension de la notion de variables. Vérifiez ensuite via la console ou via l'onglet Variable de Thonny.
1
2
3
4
5 |
|
...CORRECTION...
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 entre une valeur de départ incluse et une valeur de fin incluse.
Cette fonction doit recevoir deux entrées : la première est la valeur de départ autorisée, et la deuxième la valeur finale autorisée. Ainsi en fournissant 5 et 8, on 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 ceci (notez bien qu'on importe randint() depuis le module, pas tout le module) :
>>> 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.
13° 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
14° 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 :
15° 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.
16° 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 : 13 07 2023
Auteur : ows. h.