Le terme "closure" a été emprunté à la programmation fonctionnelle et est présent dans Python depuis le début. Les closures permettent à une fonction de mémoriser l'environnement dans lequel elle a été créée, même si elle est appelée en dehors de cet environnement. Ce concept offre de la flexibilité et permet de mettre en œuvre de nombreux modèles, y compris les usines de fonctions et le calcul paresseux.
En Python, les fonctions sont des objets de premier ordre. Parfois, il est nécessaire qu'une fonction imbriquée utilise des variables de la portée de la fonction englobante, même après sa fin. La portée lexicale ordinaire ne garantit pas cela lors du retour de la fonction. Si une telle fonction accède aux variables de son contexte de création, une closure se forme.
Une closure se forme si la fonction interne se réfère à des variables définies dans l'extérieur, et que cette extérieure a retourné la fonction interne vers l'extérieur. Cela est souvent utilisé pour créer des usines de fonctions, encapsuler des états sans classes et construire des fonctions avec des paramètres "sur place".
def make_multiplier(factor): def multiplier(x): return x * factor return multiplier mul2 = make_multiplier(2) mul3 = make_multiplier(3) print(mul2(10)) # 20 print(mul3(10)) # 30
nonlocal est utilisé.Une closure peut-elle conserver un état mutable entre les appels, si la variable est modifiée dans la fonction imbriquée ?
Oui, si dans la fonction imbriquée, on utilise le mot-clé nonlocal. Sans nonlocal, l'assignation crée une nouvelle variable locale, sans modifier l'extérieur.
def counter(): count = 0 def inc(): nonlocal count count += 1 return count return inc c = counter() print(c()) # 1 print(c()) # 2
Peut-on implémenter des variables privées en Python en utilisant des closures à la place des classes ?
Oui, une closure offre une implémentation simple de "variables privées", inaccessibles de l'extérieur, à moins que des getters/setters ne soient fournis dans la fonction imbriquée.
Les closures sont-elles appliquées uniquement aux fonctions ? Peut-on organiser une closure avec des lambdas en Python ?
Oui, une closure peut aussi se former avec des expressions lambda, car elles sont analogues à def en ce qui concerne la liaison des variables lexiques.
def make_power(n): return lambda x: x ** n square = make_power(2) cube = make_power(3) print(square(4)) # 16 print(cube(2)) # 8
nonlocal.Une usine de fonctions formant des gestionnaires dans une boucle utilise une variable de boucle à l'intérieur d'une closure:
handlers = [] for i in range(3): def handler(x): return x + i handlers.append(handler) print([h(10) for h in handlers]) # [12, 12, 12]
Avantages :
Inconvénients :
i, sa dernière valeur étant 2 — un comportement inattendu pour la plupart.Un argument par défaut a été utilisé pour "fixer" la valeur :
handlers = [] for i in range(3): def handler(x, j=i): return x + j handlers.append(handler) print([h(10) for h in handlers]) # [10, 11, 12]
Avantages :
Inconvénients :