276 votes

Fermetures en Python

J'ai vu et utilisé des fonctions imbriquées en Python. Ils correspondent à la définition d'une fermeture.

Ce n'est pas la fermeture simplement parce qu'elle n'est pas utilisée par le monde extérieur?

MISE À JOUR: Je lisais à propos des fermetures et cela m'a fait penser à ce concept en ce qui concerne Python. Un peu de recherche m'a amené à l'article de quelqu'un dans les commentaires. Mais je ne pouvais pas comprendre complètement l'explication là-bas. J'ai pensé à demander aux experts ici. Donc là...

430voto

aaronasterling Points 25749

La fermeture se produit lorsqu'une fonction a accès à une variable locale à partir d'un cadre englobant qui a fini son exécution.

def make_printer(msg):
    def printer():
        print msg
    return printer

printer = make_printer('Foo!')
printer()

Lors de l' make_printer est appelé, une nouvelle image est mis sur la pile avec le code compilé pour l' printer fonction comme une constante, la valeur de msg comme un local. Ensuite, il crée et renvoie la fonction. Parce que la fonction printer références l' msg de la variable, il est maintenu en vie après l' make_printer fonction est de retour.

Donc, si vos fonctions imbriquées ne sont pas

  1. accéder aux variables locales à enfermer étendues,
  2. le faire lorsqu'elles sont exécutées en dehors du champ d'application,

alors ils ne sont pas des fermetures.

Voici un exemple d'une fonction imbriquée qui n'est pas une fermeture.

def make_printer(msg):
    def printer(msg=msg):
        print msg
    return printer

printer = make_printer("Foo!")
printer()  #Output: Foo!

Ici, nous sommes à la liaison de la valeur à la valeur par défaut d'un paramètre. Cela se produit lorsque la fonction printer est créé et donc pas de référence à la valeur de msg externes à l' printer doit être maintenu après l' make_printer de rendement. msg est un variable locale de la fonction printer dans ce contexte.

113voto

sjcipher Points 2626

La question a déjà été répondu par aaronasterling

Cependant, quelqu'un pourrait être intéressé par la façon dont les variables sont stockées sous le capot.

Avant de venir à l'extrait de code:

Les fermetures sont des fonctions qui héritent des variables à partir de leur joignant l'environnement. Lorsque vous passez un rappel de fonction comme argument d'une autre fonction qui va faire des I/O, cette fonction de callback sera appelé plus tard, et cette fonction sera - presque comme par magie - vous rappeler le contexte dans lequel il a été déclaré, avec toutes les variables disponibles dans ce contexte.

  • Si une fonction n'utilise pas de variables libres, il ne se forme pas une fermeture.

  • Si il y a un autre niveau interne qui utilise des variables libres -- tous les niveaux précédents sauver l'environnement lexical ( par exemple à la fin )

  • fonction des attributs func_closure en python < 3.X ou __closure__ en python > 3.X enregistrer les variables libres.

  • Chaque fonction en python a cette fermeture attributs, mais il n'a pas d'enregistrer tout contenu si il n'y a pas de variables libres.

exemple: de la fermeture des attributs, mais pas de contenu à l'intérieur comme il n'y a pas de variable libre.

>>> def foo():
...     def fii():
...         pass
...     return fii
...
>>> f = foo()
>>> f.func_closure
>>> 'func_closure' in dir(f)
True
>>>

NB: GRATUIT VARIABLE EST DE CRÉER UN SYSTÈME DE FERMETURE.

Je vais expliquer à l'aide du même extrait que ci-dessus:

>>> def make_printer(msg):
...     def printer():
...         print msg
...     return printer
...
>>> printer = make_printer('Foo!')
>>> printer()  #Output: Foo!

Et toutes les fonctions Python ont une fermeture attribut donc, nous allons examiner les enfermant des variables associées à une fonction de clôture.

Ici est l'attribut func_closure pour la fonction printer

>>> 'func_closure' in dir(printer)
True
>>> printer.func_closure
(<cell at 0x108154c90: str object at 0x108151de0>,)
>>>

L' closure attribut renvoie un tuple de la cellule des objets qui contiennent des détails sur les variables définies dans le cadre englobant.

Le premier élément dans la func_closure qui pourrait être rien ou un n-uplet de cellules qui contiennent des liaisons pour la fonction de variables libres et il est en lecture seule.

>>> dir(printer.func_closure[0])
['__class__', '__cmp__', '__delattr__', '__doc__', '__format__', '__getattribute__',
 '__hash__', '__init__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', 
 '__setattr__',  '__sizeof__', '__str__', '__subclasshook__', 'cell_contents']
>>>

Ici, au-dessus de la sortie, vous pouvez voir l' cell_contents, voyons ce que les magasins de:

>>> printer.func_closure[0].cell_contents
'Foo!'    
>>> type(printer.func_closure[0].cell_contents)
<type 'str'>
>>>

Donc, quand nous avons appelé la fonction printer(), il accède à la valeur stockée à l'intérieur de l' cell_contents. C'est ainsi que nous avons eu la sortie 'Foo!'

De nouveau, je vais vous expliquer à l'aide de l'extrait ci-dessus, avec quelques modifications:

 >>> def make_printer(msg):
 ...     def printer():
 ...         pass
 ...     return printer
 ...
 >>> printer = make_printer('Foo!')
 >>> printer.func_closure
 >>>

Dans l'extrait ci-dessus, j'din pas imprimer msg à l'intérieur de l'imprimante de la fonction, de sorte qu'il ne crée pas de variable libre. Comme il n'existe pas de variable, il n'y aura pas de contenu à l'intérieur de la clôture. C'est exactement ce que nous voyons ci-dessus.

Maintenant je vais vous expliquer un autre extrait pour effacer tout Free Variable avec Closure:

>>> def outer(x):
...     def intermediate(y):
...         free = 'free'
...         def inner(z):
...             return '%s %s %s %s' %  (x, y, free, z)
...         return inner
...     return intermediate
...
>>> outer('I')('am')('variable')
'I am free variable'
>>>
>>> inter = outer('I')
>>> inter.func_closure
(<cell at 0x10c989130: str object at 0x10c831b98>,)
>>> inter.func_closure[0].cell_contents
'I'
>>> inn = inter('am')

Nous voyons donc qu'un func_closure de la propriété est un tuple de la fermeture des cellules, nous pouvons nous référer à eux et leur contenu de façon explicite-une cellule a des biens "cell_contents"

>>> inn.func_closure
(<cell at 0x10c9807c0: str object at 0x10c9b0990>, 
 <cell at 0x10c980f68: str object at   0x10c9eaf30>, 
 <cell at 0x10c989130: str object at 0x10c831b98>)
>>> for i in inn.func_closure:
...     print i.cell_contents
...
free
am 
I
>>>

Ici, lorsque nous les avons appelés inn, il sera de renvoyer tous les gagner gratuitement des variables ainsi nous obtenons I am free variable

>>> inn('variable')
'I am free variable'
>>>

80voto

Cristian Garcia Points 805

Python a un faible soutien de la fermeture. Pour voir ce que je veux dire, prenons l'exemple suivant d'un compteur à l'aide de la fermeture avec le JavaScript:

function initCounter(){
    var x = 0;
    function counter  () {
        x += 1;
        console.log(x);
    };
    return counter;
}

count = initCounter();

count(); //Prints 1
count(); //Prints 2
count(); //Prints 3

La fermeture est très élégant car il donne les fonctions de l'écrit, la capacité à avoir une "mémoire interne". Comme de Python 2.7, ce n'est pas possible. Si vous essayez

def initCounter():
    x = 0;
    def counter ():
        x += 1 ##Error, x not defined
        print x
    return counter

count = initCounter();

count(); ##Error
count();
count();

Vous recevrez un message d'erreur disant que x n'est pas définie. Mais comment cela peut-il être si il a été montré par d'autres personnes que vous pouvez l'imprimer? C'est parce que de la façon Python il gère les fonctions de la portée des variables. Alors que la fonction interne peut lire à l'extérieur de la fonction de variables, il ne peut pas écrire .

C'est une honte vraiment. Mais, avec seulement en lecture seule fermeture, vous pouvez au moins mettre en œuvre la fonction de décorateur motif pour lequel Python propose sucre syntaxique.

9voto

fpmora Points 21

J'avais une situation où j'avais besoin d'un espace de nom séparé mais persistant. J'ai utilisé des classes. Je ne sais pas autrement Les noms distincts mais persistants sont des noms fermés.

 >>> class f2:
...     def __init__(self):
...         self.a = 0
...     def __call__(self, arg):
...         self.a += arg
...         return(self.a)
...
>>> f=f2()
>>> f(2)
2
>>> f(2)
4
>>> f(4)
8
>>> f(8)
16

# **OR**
>>> f=f2() # **re-initialize**
>>> f(f(f(f(2)))) # **nested**
16

# handy in list comprehensions to accumulate values
>>> [f(i) for f in [f2()] for i in [2,2,4,8]][-1] 
16
 

6voto

Krcn U Points 35
 def nested1(num1): 
    print "nested1 has",num1
    def nested2(num2):
        print "nested2 has",num2,"and it can reach to",num1
        return num1+num2    #num1 referenced for reading here
    return nested2
 

Donne:

 In [17]: my_func=nested1(8)
nested1 has 8

In [21]: my_func(5)
nested2 has 5 and it can reach to 8
Out[21]: 13
 

Cet exemple donnerait une idée claire de ce qu'est la fermeture et comment elle peut être utilisée. Il est important de savoir, même si vous n'avez pas besoin de l'utiliser, car ils peuvent le demander lors d'un entretien d'embauche.

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