Variables locales globales effets de bords
La portée des variables⚓︎
Exemple 1
⌛ Avant d'exécuter le programme ci-dessous, interrogez-vous sur la valeur de la variable i
à l'issue de l’exécution, puis lancer le code et vérifiez.
🌵 Cliquez ici pour comprendre ...
Comme vous avez pu le constater, nous avons eu droit à une erreur : "NameError: name 'i' is not defined"
.
Pourquoi cette erreur, la variable i est bien définie dans la fonction fct()
et la fonction fct()
est bien exécutée, où est donc le problème ?
En fait, la variable i est une variable dite locale : elle a été définie dans une fonction et elle "restera" dans cette fonction.
Une fois que l'exécution de la fonction sera terminée, la variable i sera "détruite" (supprimée de la mémoire). Elle n'est donc pas accessible depuis "l'extérieur" de la fonction (ce qui explique le message d'erreur que nous obtenons).
Une variable définie dans une fonction est locale. Elle ne peut pas être utilisée en dehors de la fonction.
Exemple 2
⌛ Avant d'exécuter le programme ci-dessous, interrogez-vous sur ce qui va s'afficher, puis lancez le code et vérifiez.
# Tests
(insensible à la casse)(Ctrl+I)
(Ctrl+Clic pour inverser les colonnes)
🌵 Cliquez ici pour comprendre ...
Ici, pas de message d'erreur.
En fait, la variable i est ici une variable dite globale : elle a été définie à l'extérieur de le fonction.
La fonction peut y accéder.
Exemple 3
⌛ Avant d'exécuter le programme ci-dessous, interrogez-vous sur ce qui va s'afficher, puis lancez le code et vérifiez.
# Tests
(insensible à la casse)(Ctrl+I)
(Ctrl+Clic pour inverser les colonnes)
🌵 Cliquez ici pour comprendre ...
😢 Cela se complique ...
Pourtant cela ne semble pas très différent de l'exemple précédant.
Pourtant il y a une énorme différence : dans la fonction, on a : i =
Il a donc été créé une variable locale i
qui malheureusement porte le même nom qie la variable globale i
.
Dans cette fontion, on ne connait pas cette variable locale, ce qui explique le message d'erreur.
Exemple 4
⌛ Avant d'exécuter le programme ci-dessous, interrogez-vous sur ce qui va s'afficher, puis lancez le code et vérifiez.
# Tests
(insensible à la casse)(Ctrl+I)
(Ctrl+Clic pour inverser les colonnes)
🌵 Cliquez ici pour comprendre ...
Cette fois pas d'erreur, mais à la fin de l'exécution de ce programme, la variable i
référence la valeur 3.
En fait, dans cet exemple nous avons 2 variables i
différentes : la variable i "globale" (celle qui a été définie en dehors de toute fonction) et la variable i "locale" (celle qui a été définie dans la fonction). _Ces 2 variables portent le même nom (ce n'est pas une bonne idée), mais sont différentes.
👉 Une variable définie localement dans une fonction peut porter le même nom qu'une variable globale. Dans ce cas, ce sont deux variable distinctes mais de même nom (ce qui n'est pas une bonne pratique).
Exemple 5
⌛ Avant d'exécuter le programme ci-dessous, interrogez-vous sur ce qui va s'afficher, puis lancez le code et vérifiez.
# Tests
(insensible à la casse)(Ctrl+I)
(Ctrl+Clic pour inverser les colonnes)
🌵 Cliquez ici pour comprendre ...
Pour le print("Dans la fonction i = ", i)
situé dans la fonction le système trouve une variable i
dans l'espace local de la fonction "fct"
, et il utilise donc celle ci, même s'il existe une variable globale i
.
Il est important de bien comprendre que lorsque le système trouve une variable i dans l'espace local de la fonction, la "recherche" de la variable i s'arrête là.
D'autre part, ici nous avons deux variable i
: l'une globale, définie en dehors de fct()
, une autre locale, définie dans fct()
.
Lorsque la fonction s'arrête, la variable locale est détruite, la variable globale demeure et n'a pas été modifiée.
Visualisons dans Python tutor :
Exemple 6
⌛ Avant d'exécuter le programme ci-dessous, interrogez-vous sur ce qui va s'afficher, puis lancez le code et vérifiez.
Regardez de nouveau les exemples précédants. N'avons-nous pas déjà rencontré un exemple du même type ?
# Tests
(insensible à la casse)(Ctrl+I)
(Ctrl+Clic pour inverser les colonnes)
🌵 Cliquez ici pour comprendre ...
😊 Reportez-vous à l'exemple 3 ...
Exemple 7
⌛ Avant d'exécuter le programme ci-dessous, interrogez-vous sur ce qui va s'afficher, puis lancez le code et vérifiez.
Regardez de nouveau les exemples précédants. N'avons-nous pas déjà rencontré un exemple du même type ?
# Tests
(insensible à la casse)(Ctrl+I)
(Ctrl+Clic pour inverser les colonnes)
🌵 Cliquez ici pour comprendre ...
Nous avons juste rajouté la ligne global i
dans la fonction.
Cela change tout car la variable i
utilisée dans la fonction, est la variable globale i
.
⚠️ Cette fonction va donc modifier une variable globale définie à l'extérieur de l fonction. C'est souvent très dangereux et déconseillé.
C'est parfois utile quand même. Pour pouvoir modifier une variable globale, il faut utiliser dans la fonction l'instruction global
🌵 En fait, l'utilisation de "global" est une (très) mauvaise pratique, car cette utilisation entraîne des "effets de bord".
👓 Observez ce qu'il se passe, et ce qui diffère de l'exemple 5
Effets de bords⚓︎
Effet de bord
En informatique, une fonction est dite à effet de bord (traduction mot à mot de l'anglais side effect, dont le sens est plus proche d'effet secondaire) si elle modifie un état en dehors de son environnement local (modifie l'état d'une variable globale).
Source : Wikipedia
Exemple 1
Reprenons l'exemple précédent :
# Tests
(insensible à la casse)(Ctrl+I)
(Ctrl+Clic pour inverser les colonnes)
Dans notre exemple ci-dessus la fonction fct
modifie bien la valeur référencée par la variable i
:
- Avant l'exécution de
fct
, la variablei
référence la valeur3
, - Après l'exécution de la fonction fct la variable
i
référence la valeur4
.
Nous avons donc bien un effet de bord.
🌵 Les effets de bord c'est "mal" ! Mais pourquoi est-ce "mal" ?
Les effets de bords provoquent parfois des comportements non désirés par le programmeur (évidemment dans des programmes très complexes, pas dans des cas simplistes comme celui que nous venons de voir).
Ils rendent aussi parfois les programmes difficilement lisibles (difficilement compréhensibles). À cause des effets de bord, on risque de se retrouver avec des variables qui référenceront des valeurs qui n'étaient pas prévues par le programmeur.
On dit aussi qu'à un instant donné, l'état futur des variables est difficilement prévisible à cause des effets de bord.
👉 En résumé, on évitera autant que possible l'utilisation du "global".
Un paradigme de programmation se propose d'éviter au maximum les effets de bords : la programmation fonctionnelle.
Vous étudierez ce paradigme de programmation plus tard.
👉 Dans cet exemple, l'effet de bord était volontaire et provoqué par l'utilisation de global
Exemple 2 : comparaison de deux fonctions
🐘🐘 Vous pourrez réviser ce qui concerne les copies de listes, que nous avons déjà étudiées.
- Une pure fonction sans effet de bord prenant en paramètre un entier ...
(😂 On dit réellement une "pure fonction, ou fonction pure ...)
# Tests
(insensible à la casse)(Ctrl+I)
(Ctrl+Clic pour inverser les colonnes)
👉 La variable globale a = 3
n'a pas été modifiée : il n'y a pas eu d'effet de bord.
- Le même genre de fonction mais cette fois le paramètre est une liste ...
# Tests
(insensible à la casse)(Ctrl+I)
(Ctrl+Clic pour inverser les colonnes)
👉 La variable globale lst = [1, 2, 3]
a été modifiée sans utilisation de l'instruction global
: Il y a eu effet de bord.
🌵🌵 Cet effet de bord n'était peut-être pas voulu !
- Reprenons l'exemple de la fonction pure :
- Reprenons l'exemple prenant une liste comme paramètre
pourquoi ?
La liste passée en argument est modifiée dans la fonction, mais la valeur dans l'environnement global est aussi modifiée.
Contrairement à la variable a
qui était de type int
, la variable de type list
n'est pas dupliquée : c'est la même liste qui est traitée dans la fonction.
Le tri par sélection⚓︎
🐘🐘 Vous devez absolument bien connaître ce tri
Le tri par sélection classique
- Examinons un algorithme de tri par sélection classique :
# Tests
(insensible à la casse)(Ctrl+I)
(Ctrl+Clic pour inverser les colonnes)
tri en place
La liste a été triée en place, sans utiliser de return
: on a utilisé l'effet de bord.
💡 Ici, l'effet de bord a été utilisé de façon volontaire
Cette fonction ne renvoie rien. (On appelle ces fonctions des procédure.)
En python, il n'y a pas de différence formelles entre fonctions et procédures.
- 🤔 Imaginons qu'on souhaite ne pas modifier la liste de départ et qu'on essaye simplement d'ajouter un return dans la fonction
# Tests
(insensible à la casse)(Ctrl+I)
(Ctrl+Clic pour inverser les colonnes)
Tri en place
Cela n'a rien changé, il y a toujours effet de bord : la liste initiale est quand même modifiée !
À savoir
L'algorithme de tri par sélection à connaître est une procédure qui réalise un tri en place par effet de bord
Le tri par insertion⚓︎
Le tri par insertion
Exécuter le script suivant avec la procédure (pas de return
) de tri par insertion
# Tests
(insensible à la casse)(Ctrl+I)
(Ctrl+Clic pour inverser les colonnes)
Tri en place
Pour le tri par insertion, il y a aussi effet de bord : la liste initiale a été modifiée.
À savoir
L'algorithme de tri par insertion à connaître est une procédure qui réalise un tri en place par effet de bord
Le tri natif (built in) en Python sorted
⚓︎
Le tri Python sorted
Exécuter le script suivant :
# Tests
(insensible à la casse)(Ctrl+I)
(Ctrl+Clic pour inverser les colonnes)
À savoir
La fonction sorted
ne réalise pas un tri en place et n'a pas d'effet de bord
# Tests
(insensible à la casse)(Ctrl+I)
(Ctrl+Clic pour inverser les colonnes)