Les fermetures Python expliquées : comment les fonctions imbriquées capturent les variables
Fermetures (closures) Python
Découvrez comment fonctionnent les fermetures Python, comment les définir, et pourquoi elles sont essentielles pour un code propre et efficace.
Variables non-locales dans une fonction imbriquée
Avant d'explorer les fermetures, il est crucial de comprendre les fonctions imbriquées et les variables non-locales. Une fonction définie à l'intérieur d'une autre fonction est appelée fonction imbriquée. De telles fonctions peuvent lire les variables de leur portée englobante.
En Python, ces variables non-locales sont en lecture seule par défaut. Pour les modifier, vous devez déclarer explicitement la variable comme nonlocal dans la fonction imbriquée.
Voici un exemple simple montrant une fonction imbriquée accédant à une variable non-locale.
def afficher_msg(msg):
# Fonction externe
def imprimante():
# Fonction imbriquée
print(msg)
imprimante() # Exécuter
afficher_msg("Bonjour")
Sortie
Bonjour
La fonction imbriquée imprimante() peut accéder à la variable msg depuis sa portée externe.
Définir une fonction de fermeture
Et si la fonction externe retournait la fonction imbriquée au lieu de l'appeler ? Le code ressemblerait à ceci :
def afficher_msg(msg):
def imprimante():
print(msg)
return imprimante # Retourner la fonction imbriquée
# Stocker la fonction retournée
autre = afficher_msg("Bonjour")
autre()
Sortie
Bonjour
Remarquez que la fonction imprimante se souvient encore de la valeur de msg même après que afficher_msg a fini de s'exécuter. Ce phénomène — où une fonction conserve l'accès aux variables de sa portée de définition — est appelé une fermeture en Python.
Pour illustrer davantage, supprimez la fonction originale et voyez que la fonction retournée fonctionne encore :
>>> del afficher_msg
>>> autre()
Bonjour
>>> afficher_msg("Bonjour")
Traceback (most recent call last):
... NameError: name 'afficher_msg' is not defined
La fonction retournée continue de fonctionner car elle a capturé les données nécessaires dans sa fermeture.
Quand a-t-on des fermetures ?
En Python, une fermeture existe quand une fonction imbriquée :
- est définie à l'intérieur d'une autre fonction.
- référence une variable de sa fonction englobante.
- est retournée par la fonction externe.
Quand utiliser les fermetures ?
Les fermetures offrent plusieurs avantages :
- Elles éliminent le besoin de variables globales, améliorant l'encapsulation.
- Elles fournissent une alternative légère aux classes à méthode unique, gardant le code concis.
- Elles permettent des motifs fonctionnels comme les décorateurs, où une fonction wrapper a besoin d'accéder aux arguments de la fonction originale.
Voici un exemple courant : une usine qui crée des fonctions multiplicatrices.
def creer_multiplicateur_de(n):
def multiplicateur(x):
return x * n
return multiplicateur
# Créer des multiplicateurs spécifiques
fois3 = creer_multiplicateur_de(3)
fois5 = creer_multiplicateur_de(5)
print(fois3(9)) # 27
print(fois5(3)) # 15
print(fois5(fois3(2))) # 30
Sortie
27 15 30
La syntaxe des décorateurs Python repose fortement sur les fermetures. Quand vous écrivez un décorateur, la fonction wrapper capture la fonction qu'elle décore, formant une fermeture.
Pour inspecter une fermeture, utilisez l'attribut __closure__. Il retourne un tuple d'objets cell qui contiennent les valeurs enfermées. Par exemple :
>>> creer_multiplicateur_de.__closure__
>>> fois3.__closure__
(<cell at 0x0000000002D155B8: int object at 0x000000001E39B6E0>,)
L'attribut cell_contents de chaque cellule contient la valeur réelle capturée par la fermeture :
>>> fois3.__closure__[0].cell_contents
3
>>> fois5.__closure__[0].cell_contents
5
Comprendre les fermetures vous permet d'écrire du code Python plus propre, plus modulaire, qui évite les globaux inutiles et embrasse les paradigmes fonctionnels.
Python
- Maîtriser les types de données Python : Guide pratique
- Maîtriser les opérateurs Python : Guide complet
- Maîtriser l'instruction pass en Python : Guide pratique
- Fonctions lambda en Python : Guide pratique des fonctions anonymes
- Maîtriser les dictionnaires Python : création, manipulation et techniques avancées
- Générateurs Python : itération efficace et cas d'utilisation avancés
- Maîtriser les décorateurs Python : améliorer les fonctions avec des techniques expertes
- Maîtriser la méthode strip() de Python : Guide complet et exemples pratiques
- Maîtriser les fonctions lambda Python : exemples pratiques et bonnes pratiques