Première tentative
Reprenons le code précédent
import p5
largeur = 500
hauteur = 500
abscisse = 250
ordonnee = 250
rayon = 20
def setup():
p5.size(largeur,hauteur)
def draw():
p5.background(0)
p5.circle(abscisse,ordonnee,rayon)
p5.run()
La fonction draw
est éxécutée 30 fois par seconde. Nous allons faire en sorte que la valeur de l'abscisse évolue à chaque fois.
Pour cela, vous allez faire évoluer le code de la fonction draw en le remplaçant par le code suivant
def draw():
p5.background(0)
abscisse = abscisse + 1
p5.circle(abscisse,ordonnee,rayon)
Ainsi, l'abscisse vaudra 250 à la première animation, puis 251, puis 252, etc ...
- Recopiez le code de la fonction draw ci dessus dans votre environnement Python
- Éxécutez le
- Constatez que cela ne fonctionne pas et vous affiche des erreurs ...
Mais pourquoi ces erreurs ? C'est un petit peu complexe, mais nous allons essayer de déméler cela.
Penser global, agir local
Penser global, agir local. Cette formule employée par René DUBOS lors du premier sommet sur l'environnement en 1972 résonne tout à fait pour la programmation en python, mais pour d'autres raisons. Dans le langage Python comme dans d'autres langages, les variables ont ce que l'on appelle une portée. La portée d'une variable, c'est la zone dans laquelle elle est utilisable, c'est à dire lisible, modifiable.
Vous vous souvenez de nos variables ? Ce sont des boîtes étiquetées dans lesquelles on stocke des informations. À chaque fois que Python apelle une fonction, il lui fabrique ce que l'on appelle un espace de nom, c'est à dire une nouvelle étagère avec des boîtes prètes à être étiquetées et remplies. Imaginez que la fonction est en quelquesorte une pièce, et à l'intérieur, il y a cette étagère. Les variables qui sont à l'intérieur de la fonction sont appelées variables locales. Essayez le code ci dessous, vous allez comprendre
Voilà. Nous avons un début d'explication. Ce code nous affiche 10, puis 7, puis 10 de nouveau. Pourquoi ?
Nous commençons par exécuter le code en affichant la valeur de a qui est 10. Jusque là , tout va bien. Puis ensuite, nous appelons la
fonction change_a
. Celle ci fixe la valeur de a et l'affiche. La variable a vaut 7. Mais voilà, juste après, nous affichons a qui vaut ... 10 !
Ce qui se passe à Végas reste à Végas
Une fonction, c'est un peu comme Las Vegas dans les séries américaines : "Ce qui se passe à Végas reste à Végas". Ce qui se passe dans la fonction reste dans la fonction,
pour ne pas risquer de tout modifier dans le programme par effet de bord. Imaginez, vous avez une variable dans votre programme qui s'appelle i
(ce n'est pas)
le nom le plus explicite, soit dit en passant). Et ce nom est de nouveau utilisé dans votre fonction pour faire une boucle ? Damned ! Vous risqueriez d'écraser le i
que vous utilisez dans votre programme. D'autant que vous utilisez tout un tas de fonctions qui viennent de bibliothèques dont vous ne lisez pas le code !
Agir local
C'est pour cela que les variables utilisées à l'intérieur de la fonction sont dites locales. Le a
qui est marqué dans la fonction est une variable locale.
Si nous reprenons l'analogie de nos boites, cela signifie que c'est une boite qui est dans l'étagère réservée à la fonction,
c'est à dire l'étagère qui est dans la pièce qui correspond à la fonction. Il y a deux variables a
au cours de la vie du programme :
- celle qui est dans la fonction
- celle qui est dans le programme principal
change_a
)
Penser global
Oui, mais si l'on veut changer le contenu de notre variable globale? Essayez le programme ci dessous et constatez l'erreur
Vous obtenez l'erreur suivante :
local variable 'a' referenced before assignment on line 6
Qu'est ce que cela signifie ? Dans la fonction que nous avons défini, le programme Python voit un
a = ...
. Il en conclue donc qu'il doit créer une variable locale a puisque l'on va mettre quelque chose dedans.
Oui, mais à droite du signe =
, il y a une expresssion qui contient a
...
Et là, le programme Python est bien embété. Il a une variable locale a
qu'il vient de créer. Elle n'a pour l'instant aucun
contenu. Et donc lorsque l'on écrit
a = a + 3
, il n'a aucun contenu à ajouter à 3, d'où cette erreur.
Il existe une solution pour contourner cela. Attention, cette solution ne doit être utilisée qu'en dernier recours (avant, on doit voir si passer des paramètres à la fonction
n'est pas une meilleure idée). Il faut indiquer à la fonction que la variable que l'on veut modifier n'est pas une variable locale à la fonction mais une variable de l'espace
global du programme. Et pour cela on utilise le mot clef ... global
! Voici donc le code qui convient et que vous allez essayer :
seconde tentative
Cette fois le code marche et on a bien modifié la variable a qui est instanciée au début du programme, avec comme affichage 10, 13 puis 13.
Nous avons donc maintenant la solution pour notre algoblob ! Sa position était définie de façon globale, et il faut indiquer à la fonction draw
que l'on va modifier l'abscisse qui est une variable globale.
- Modifiez le programme en ajoutant la ligne
global abscisse
comme indiqué ci dessous - Éxécutez le programme
- Éprouvez ce sentiment de satisfaction d'avoir réalisé un programme qui fonctionne et d'avoir appris de nouvelles choses
import p5
largeur = 500
hauteur = 500
abscisse = 250
ordonnee = 250
rayon = 20
def setup():
p5.size(largeur,hauteur)
def draw():
global abscisse
p5.background(0)
p5.circle(abscisse,ordonnee,rayon)
p5.run()
Vous pouvez être fiers : vous avez réussi à animer votre premier algoblob, tout en apprenant plein de choses nouvelles.
- Les variables définies dans une fonction sont locales à la fonction, c'est à dire qu'elle n'existent que pendant le déroulement de celle ci
- Une variable définie à l'échelle du programme, en dehors des fonctions, est dite globale
- Un programme ne peut pas modifier une variable globale dans une fonction, sauf s'il indique avant dans la fonction qu'il va utiliser
cette variable globale grâce au mot clef
global
Il reste un petit détail que les plus attentifs auront remarqué. Dans notre fonction draw
, nous utilisons aussi les variables
ordonnee
, largeur
et hauteur
par exemple. Oui, mais nous ne faisons que les utiliser en lecture, pas pour les modifier.
Si Python trouve dans une fonction une variable qui est utilisée et qui n'est pas définie localement, mais qui n'est pas modifiée, il va chercher à trouver
cette variable au niveau global. C'est ce qu'il a fait ici. Et c'est pour cela que notre fonction draw
marchait jusqu'ici : elle utilisait les variables
mais elle ne les modifiait pas.
pour aller plus loin ...
la gestion de la portée des variables diffère beaucoup suivant les langages. Tous possèdent une notion de portée, de variable locale, de variables globales (et même pour PHP de "super globales"), mais la gestion des situations est très variable (chaines de dépendance, déclaration stricte de l'instanciation des variables, etc..)
import math
et une ligne du type
rayon = 20 + 5 * math.cos(frame_count)
(n'oubliez pas le global pour le rayon !))
Et pour la suite ?
Vous avez remarqué ? Notre algoblob est vivant! Il se déplace. Oui, mais nous n'avons pas été très fin : il s'est fait la malle par la droite de notre fenêtre et a échappé à tout contrôle. À l'instar du Docteur FRANKENSTEIN, nous avons animé notre créature mais en avons perdu le contrôle. Cela ne doit pas être et nous allons voir comment y remédier.