Le mot-clé nonlocal permet de gérer la portée des variables dans les fonctions imbriquées. Il permet de modifier des variables qui ne sont ni dans la fonction locale actuelle ni dans l'espace global.
Portée des variables
Les variables Python existent dans des portées différentes, qu'on appelle aussi des scopes. Une variable peut être locale à une fonction ou global au module.
Le mot-clé nonlocal est utilisé dans les fonctions imbriquées. Il permet à une fonction interne de modifier une variable de la fonction parente, qui n'est pas globale.
def fonction_externe(): compteur = 0 def fonction_interne(): # Sans 'nonlocal', Python crée une nouvelle variable locale 'compteur' compteur = 10 print(f"Dans la fonction interne : {compteur}") fonction_interne() print(f"Dans la fonction externe : {compteur}") fonction_externe() # Affiche : # Dans la fonction interne : 10 # Dans la fonction externe : 0
Dans cet exemple, la variable compteur de la fonction_externe n'a pas été modifiée, car Python considère compteur = 10 comme une nouvelle variable dans l'espace local de la fonction_interne.
Le mot-clé nonlocal indique que nous souhaitons modifier la variable de la fonction englobante la plus proche :
def fonction_externe(): compteur = 0 def fonction_interne(): # On déclare que 'compteur' fait référence à la variable de la fonction externe nonlocal compteur compteur = 10 print(f"Dans la fonction interne : {compteur}") fonction_interne() print(f"Dans la fonction externe : {compteur}") fonction_externe() # Affiche : # Dans la fonction interne : 10 # Dans la fonction externe : 10
C'est bien la variable compteur de la fonction_externe qui est modifiée.
nonlocal ne se limite pas à la fonction parente immédiate. Il remontera les fonctions imbriquées jusqu'à trouver la variable, sans aller jusqu'à l'espace global.
def niveau1(): x = "niveau 1" def niveau2(): y = "niveau 2" def niveau3(): nonlocal x # Accède à x de niveau1 (2 niveaux plus haut) x = "modifié depuis niveau3" niveau3() niveau2() print(f"x dans niveau1 : {x}") niveau1() # x dans niveau1 : modifié depuis niveau3
Quand utiliser nonlocal ?
nonlocal est utilisé pour la gestion d'état avec les closures.
Une closure est une fonction qui se souvient dans l'environnement dans lequel elle a été créée.
def creer_compteur(): count = 0 def incrementer(): nonlocal count count += 1 return count return incrementer # Chaque compteur "se souvient" de sa propre valeur compteur1 = creer_compteur() compteur2 = creer_compteur() print(compteur1()) # 1 print(compteur1()) # 2 print(compteur2()) # 1 (indépendant du premier)
L'exemple montre bien que la fonction interne capture la variable de la fonction qui l'entoure.