Les closures sont un mécanisme puissant de Python qui permet de créer des fonctions avec des données "mémorisées" du contexte environnant.
Dans les langages fonctionnels et en Python, les fonctions sont des objets de premier niveau. Une fonction peut retourner une nouvelle fonction qui "ferme" les variables de son environnement (portée externe).
Les développeurs se mélangent souvent les pinceaux sur la façon dont les variables fermées "vivent" à l'intérieur d'une closure, quand elles sont lues ou écrites, et comment travailler en toute sécurité avec elles (en particulier avec les objets mutables).
Si la fonction interne utilise des variables de la fonction externe, elles sont automatiquement "mémorisées" — même si la fonction externe a déjà fini son exécution. Il est évident que pour lire une variable, mais si on veut changer sa valeur — il faut utiliser le mot-clé nonlocal. Lorsqu'on travaille avec des objets mutables (listes, dictionnaires), c'est une zone de risque particulière.
Exemple :
def outer(): count = 0 def inner(): nonlocal count count += 1 return count return inner counter = outer() print(counter()) # 1 print(counter()) # 2
Caractéristiques clés :
nonlocal (sinon, cette opération crée une variable locale).Peut-on modifier la valeur d'une variable fermée à l'intérieur d'une fonction sans nonlocal ?
Non. Si on essaie d'assigner une nouvelle valeur sans indiquer nonlocal, Python interprète cela comme la création d'une nouvelle variable locale, et l'ancienne valeur ne sera pas propagée à l'extérieur.
Exemple :
def make_counter(): count = 0 def inner(): count += 1 # Erreur UnboundLocalError ! return count return inner
Peut-on passer des arguments dans le scope externe via une closure ?
Oui, la closure "mémorisera" toutes les variables accessibles dans la portée externe, y compris les attributs de classes, les variables globales, etc. Mais modifier ces variables demande des efforts spéciaux (comme l'utilisation de nonlocal ou global).
Que se passe-t-il avec les objets mutables à l'intérieur d'une closure ?
Si une variable fermée fait référence à un objet mutable, par exemple, une liste, vous pouvez modifier son contenu sans nonlocal, mais si vous essayez de réassigner la variable — nonlocal sera nécessaire.
Exemple :
def make_appender(): result = [] def append(x): result.append(x) # Cela fonctionne ! return result return append f = make_appender() print(f(1)) # [1] print(f(2)) # [1, 2]
Un développeur écrit une closure, modifie une variable sans nonlocal — une UnboundLocalError se produit.
Avantages :
Inconvénients :
Utilisation explicite de nonlocal pour un état contrôlé dans une closure.
Avantages :
Inconvénients :