90 votes

Pouvez-vous expliquer les fermetures (en rapport avec Python) ?

J'ai lu beaucoup de choses sur les fermetures et je pense les comprendre, mais sans vouloir brouiller les pistes pour moi et pour les autres, j'espère que quelqu'un pourra expliquer les fermetures de la manière la plus succincte et la plus claire possible. Je cherche une explication simple qui pourrait m'aider à comprendre où et pourquoi je voudrais les utiliser.

101voto

J.F. Sebastian Points 102961

Fermeture sur fermeture

Les objets sont des données avec des méthodes attachées, les fermetures sont des fonctions avec données attachées.

def make_counter():
    i = 0
    def counter(): # counter() is a closure
        nonlocal i
        i += 1
        return i
    return counter

c1 = make_counter()
c2 = make_counter()

print (c1(), c1(), c2(), c2())
# -> 1 2 1 2

6 votes

Notez que nonlocal a été ajouté dans python 3. python 2.x ne disposait pas de fermetures complètes en lecture-écriture (c'est-à-dire que vous pouviez lire des fermetures sur des variables, mais pas modifier leurs valeurs).

6 votes

@JamesPorter : note : vous pouvez émuler nonlocal dans Python 2 en utilisant un objet mutable, par exemple, L = [0] \n def counter(): L[0] += 1; return L[0] c'est-à-dire que vous ne pouvez pas changer le nom (le lier à un autre objet) dans ce cas. mais vous pouvez changer l'objet mutable lui-même auquel le nom fait référence. La liste est nécessaire car les entiers sont immuables en Python.

1 votes

@J.F.Sebastian : c'est vrai. cela ressemble toujours à un sale, sale hack cependant :)

46voto

Anders Eurenius Points 2976

C'est simple : Une fonction qui fait référence à des variables d'un périmètre contenant, potentiellement après que le flux de contrôle ait quitté ce périmètre. Cette dernière partie est très utile :

>>> def makeConstantAdder(x):
...     constant = x
...     def adder(y):
...         return y + constant
...     return adder
... 
>>> f = makeConstantAdder(12)
>>> f(3)
15
>>> g = makeConstantAdder(4)
>>> g(3)
7

Notez que 12 et 4 ont "disparu" à l'intérieur de f et g, respectivement, cette caractéristique est ce qui fait de f et g des fermetures appropriées.

0 votes

Il n'est pas nécessaire de faire constant = x ; vous pourriez juste faire return y + x dans la fonction imbriquée (ou recevoir l'argument avec le nom constant ), et cela fonctionnerait très bien ; les arguments sont capturés par la fermeture de la même manière que les locales sans argument.

15voto

ESV Points 4591

J'aime cette définition brute et succincte :

Une fonction qui peut faire référence à des environnements qui ne sont plus actifs.

J'ajouterais

Une fermeture permet de lier des variables à une fonction. sans les passer comme paramètres .

Les décorateurs qui acceptent des paramètres sont une utilisation courante des fermetures. Les fermetures sont un mécanisme d'implémentation courant pour ce type de "fabrique de fonctions". Je choisis fréquemment d'utiliser les closures dans les Modèle de stratégie lorsque la stratégie est modifiée par des données au moment de l'exécution.

Dans un langage qui permet la définition de blocs anonymes (par exemple, Ruby, C#), les fermetures peuvent être utilisées pour implémenter (ce qui revient à) de nouvelles structures de contrôle. L'absence de blocs anonymes fait partie des les limites des fermetures en Python .

15voto

Jegschemesch Points 4093

Pour être honnête, je comprends parfaitement les fermetures sauf que je n'ai jamais été clair sur ce qu'est exactement la chose qui est la "fermeture" et ce qui est si "fermeture" à son sujet. Je vous recommande d'abandonner la recherche d'une quelconque logique derrière le choix du terme.

Bref, voici mon explication :

def foo():
   x = 3
   def bar():
      print x
   x = 5
   return bar

bar = foo()
bar()   # print 5

L'idée clé ici est que l'objet fonction retourné par foo conserve un lien avec la var locale 'x' même si 'x' est sorti de la portée et devrait être défectueux. Ce lien concerne la variable elle-même, et pas seulement la valeur qu'elle avait à ce moment-là. Ainsi, lorsque bar est appelé, il imprime 5 et non 3.

Il faut également savoir que Python 2.x a une fermeture limitée : il n'y a aucun moyen de modifier 'x' à l'intérieur de 'bar' parce qu'écrire 'x = bla' déclarerait un 'x' local dans bar, et non une affectation à 'x' de foo. Il s'agit d'un effet secondaire de la déclaration assignment=de Python. Pour contourner ce problème, Python 3.0 introduit le mot-clé nonlocal :

def foo():
   x = 3
   def bar():
      print x
   def ack():
      nonlocal x
      x = 7
   x = 5
   return (bar, ack)

bar, ack = foo()
ack()   # modify x of the call to foo
bar()   # print 7

7voto

Mark Cidade Points 53945

Je n'ai jamais entendu parler de transactions utilisées dans le même contexte que l'explication de ce qu'est une fermeture et il n'y a pas vraiment de sémantique de transaction ici.

On l'appelle fermeture parce qu'elle "ferme" la variable (constante) extérieure - c'est-à-dire qu'il ne s'agit pas seulement d'une fonction mais d'une fermeture de l'environnement où la fonction a été créée.

Dans l'exemple suivant, l'appel de la fermeture g après avoir modifié x changera également la valeur de x dans g, puisque g se ferme sur x :

x = 0

def f():
    def g(): 
        return x * 2
    return g

closure = f()
print(closure()) # 0
x = 2
print(closure()) # 4

0 votes

Aussi, tel qu'il est, g() calcule x * 2 mais ne renvoie rien. Cela devrait être return x * 2 . +1 néanmoins pour une explication du mot "fermeture".

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X