Portée des variables (scope)
La portée (scope) d'une variable détermine les parties du code où cette variable est accessible. Comprendre la portée permet d'éviter des bugs subtils et d'écrire du code plus clair.
1. Variables locales
Une variable locale est créée à l'intérieur d'une fonction. Elle n'existe que pendant l'exécution de cette fonction et n'est accessible que dans son corps.
Exemple de base
def ma_fonction():
x = 10 # Variable locale
print("Dans la fonction, x =", x)
ma_fonction() # Affiche 10
print(x) # ❌ ERREUR : x n'existe pas ici
Que se passe-t-il ?
- Quand
ma_fonction()est appelée, Python crée une variablexlocale - À la fin de l'exécution de la fonction,
xest détruite print(x)cherchexet ne la trouve pas → erreurNameError
Les paramètres sont aussi des variables locales
def calculer(a, b):
# a et b sont des variables locales
resultat = a + b # resultat est aussi locale
return resultat
somme = calculer(5, 3)
print(somme) # 8
print(a) # ❌ ERREUR : a n'existe que dans calculer()
Pourquoi les variables locales ?
Les variables locales permettent d'isoler le code de chaque fonction. Deux fonctions peuvent avoir des variables du même nom sans conflit :
2. Variables globales
Une variable globale est définie en dehors de toute fonction, au niveau principal du script. Elle est accessible partout dans le fichier.
Lire une variable globale
a = 5 # Variable globale
def afficher_a():
print("Dans la fonction, a =", a) # On peut lire a
afficher_a() # 5
print("En dehors, a =", a) # 5
Ici, pas de problème : on lit simplement la valeur de a.
Le piège : essayer de modifier sans global
x = 10 # Variable globale
def incrementer_x():
x = x + 1 # ❌ ERREUR : UnboundLocalError
print(x)
incrementer_x()
Que se passe-t-il ?
Python voit x = ... dans la fonction et décide que x est une variable locale pour toute la fonction. Donc quand il lit x + 1, il cherche une variable locale x qui n'a pas encore été créée → erreur.
Erreur conceptuelle courante
Beaucoup pensent que Python "lit d'abord la variable globale puis crée une locale", mais c'est faux. Python analyse la fonction avant de l'exécuter et décide que toute variable assignée (=) est locale. L'erreur se produit avant la lecture.
Modifier une variable globale avec global
Pour modifier une variable globale depuis une fonction, il faut déclarer explicitement global :
x = 10 # Variable globale
def incrementer_x():
global x # Déclare qu'on veut utiliser la variable globale
x = x + 1 # Maintenant ça marche
print("Avant :", x) # 10
incrementer_x()
print("Après :", x) # 11
Conflit de nommage : créer une variable locale masque la globale
compteur = 0 # Variable globale
def incrementer_compteur():
compteur = 5 # Crée une variable LOCALE "compteur"
print("Dans la fonction, compteur =", compteur)
incrementer_compteur() # Affiche 5
print("Global, compteur =", compteur) # Affiche 0 (inchangé)
Ici, la fonction crée une nouvelle variable locale compteur qui masque la globale. La variable globale reste à 0.
3. Bonnes pratiques
Éviter les variables globales modifiables
Les variables globales modifiables rendent le code difficile à comprendre et à débugger :
- N'importe quelle fonction peut changer leur valeur
- Difficile de savoir qui a modifié quoi
- Crée des dépendances cachées entre fonctions
Préférez :
- Passer les valeurs en paramètres
- Retourner les résultats avec return
Les constantes globales sont OK
Les variables globales en lecture seule (constantes) sont acceptables :
PI = 3.14159
TVA = 0.20
MAX_TENTATIVES = 3
def calculer_prix_ttc(prix_ht):
return prix_ht * (1 + TVA) # Lecture de TVA : OK
Par convention, on les écrit en MAJUSCULES.
Exception : Programmation événementielle
En programmation événementielle (interfaces graphiques, jeux vidéo), il est difficile d'éviter les variables globales modifiables. Les fonctions de callback (réactions aux événements) doivent accéder à l'état de l'application.
Exemple avec tkinter (interface graphique) :
import tkinter as tk
compteur = 0 # Variable globale nécessaire
def clic():
global compteur
compteur += 1
label.config(text=f"Clics : {compteur}")
fenetre = tk.Tk()
label = tk.Label(fenetre, text="Clics : 0")
label.pack()
bouton = tk.Button(fenetre, text="Cliquer", command=clic)
bouton.pack()
fenetre.mainloop()
Pourquoi c'est acceptable ici ?
- La fonction
clic()est appelée par le framework (pas par vous) - On ne peut pas passer de paramètres à
clic()directement - Les variables globales modélisent l'état de l'application
Dans ce contexte, c'est un compromis nécessaire pour les débutants. Les programmeurs avancés utilisent des classes (programmation orientée objet) pour mieux gérer l'état, mais en première, les variables globales sont acceptables pour la programmation événementielle.
4. Exercices
Exercice 1 : Prédire le résultat
Sans exécuter le code, prédisez ce qui sera affiché :
Puis exécutez pour vérifier.
Solution
Explication : La fonction crée une variable locale y = 50 qui masque la globale. La variable globale y reste à 100.
Exercice 2 : Identifier l'erreur
Ce code provoque une erreur. Pourquoi ? Comment la corriger ?
Solution
Erreur : UnboundLocalError: local variable 'total' referenced before assignment
Pourquoi : Python voit total = ... et décide que total est locale. Quand il lit total + valeur, il cherche une variable locale qui n'existe pas encore.
Correction 1 (avec global) :
Correction 2 (meilleure : sans global) :
Exercice 3 : Gestion de stock
Créez un fichier stock.py avec :
- Une fonction
acheter(stock)qui diminue le stock de 1 et le retourne - Une fonction
livraison(stock, quantite)qui augmente le stock et le retourne
Testez avec un stock initial de 10, 2 achats, puis une livraison de 5.
Solution
def acheter(stock):
"""Diminue le stock de 1."""
return stock - 1
def livraison(stock, quantite):
"""Augmente le stock de la quantité livrée."""
return stock + quantite
# Test
stock = 10
print("Stock initial :", stock)
stock = acheter(stock)
stock = acheter(stock)
print("Après 2 achats :", stock) # 8
stock = livraison(stock, 5)
print("Après livraison de 5 :", stock) # 13
Exercice 4 : Portée dans les boucles (piège)
Que va afficher ce code ?
Puis exécutez pour vérifier. Que se passe-t-il avec x et i ?
Solution
Attention : En Python, les variables créées dans une boucle for ne sont pas locales à la boucle, elles sont locales à la fonction. Donc x et i existent encore après la boucle.
C'est différent de langages comme C, Java ou Rust où les variables de boucle sont détruites après la boucle.
Exercice 5 : Accumulateur
Sans utiliser de variable globale, écrivez une fonction somme_liste(liste) qui calcule la somme des éléments d'une liste.
Testez avec [1, 2, 3, 4, 5].
Solution
5. Résumé
| Type | Où est-elle définie ? | Accessible où ? | Exemple |
|---|---|---|---|
| Locale | Dans une fonction | Uniquement dans cette fonction | def f(): x = 5 |
| Globale | Hors de toute fonction | Partout (lecture) | x = 5 (au niveau principal) |
| Globale modifiable | Hors de toute fonction | Partout (écriture avec global) |
global x; x = 10 |
Règles importantes :
- Les paramètres d'une fonction sont des variables locales
- Une assignation
x = ...dans une fonction crée une variable locale (sauf siglobal x) - Préférez paramètres + return aux variables globales modifiables
- Les variables de boucle
foren Python ne sont pas détruites après la boucle
Pour aller plus loin : Autres langages
D'autres langages ont des règles de portée plus strictes. Par exemple, en Rust, même les variables dans une boucle ont leur propre portée :
fn main() {
let x = 10;
for i in 0..3 {
let x = x + i; // Nouvelle variable locale à la boucle
println!("Boucle : x = {}", x);
}
println!("Après : x = {}", x); // x = 10 (inchangé)
}
Sortie :
En Rust, les variables globales modifiables nécessitent un bloc unsafe car elles sont considérées dangereuses. Python est plus permissif, mais cela ne signifie pas qu'il faut en abuser !