Maison >développement back-end >Tutoriel Python >Explication détaillée des fermetures et des décorateurs en Python

Explication détaillée des fermetures et des décorateurs en Python

零下一度
零下一度original
2017-06-25 09:55:031427parcourir

La clôture est une structure grammaticale importante dans la programmation fonctionnelle. La fermeture est également une structure d'organisation du code, qui améliore également la réutilisabilité du code.

Si, dans une fonction en ligne, une référence est faite à une variable dans la fonction externe (mais pas dans la portée globale), alors la fonction en ligne est considérée comme une fermeture.

Les variables définies au sein d'une fonction externe mais référencées ou utilisées par une fonction interne sont appelées variables libres.

Pour résumer, la création d'une fermeture doit répondre aux points suivants :

  • 1 Il doit y avoir une fonction embarquée

  • . 2. La fonction intégrée doit faire référence à la variable dans la fonction externe

  • 3. La valeur de retour de la fonction externe doit être la fonction intégrée

1 . Exemple d'utilisation de la fermeture

Regardons d'abord un exemple de fermeture :

In [10]: def func(name):
    ...:     def in_func(age):
    ...:         print 'name:',name,'age:',age
    ...:     return in_func
    ...: 

In [11]: demo = func('feiyu')In [12]: demo(19)
name: feiyu age: 19

Ici, lorsque func est appelé, une fermeture est générée - in_func, et la fermeture contient la variable libre - name, donc cela signifie aussi que lorsque le cycle de vie de la fonction func se termine, la variable name existe toujours car elle est référencée par la fermeture, elle ne sera donc pas recyclée .

Dans la fonction de python, vous pouvez référencer directement des variables externes, mais vous ne pouvez pas écraser les variables externes. Par conséquent, si vous écrasez directement les variables de la fonction parent dans la fermeture, une erreur se produira. Regardez l'exemple suivant :

Exemple de mise en œuvre d'une fermeture de comptage :

def counter(start=0):count = [start] def incr():count[0] += 1return countreturn incr

a = counter()
print 'a:',aIn [32]: def counter(start=0):
    ...:     count = start
    ...:     def incr():
    ...:         count += 1
    ...:         return count
    ...:     return incr
    ...: 

In [33]: a = counter()In [35]: a()  #此处会报错

UnboundLocalError: local variable 'count' referenced before assignment

doit être utilisé comme ceci :

In [36]: def counter(start=0):
    ...:     count = [start]
    ...:     def incr():
    ...:         count[0] += 1
    ...:         return count
    ...:     return incr
    ...: 

In [37]: count = counter(5)

In [38]: for i in range(10):
    ...:     print count(),
    ...:     
[6] [7] [8] [9] [10] [11] [12] [13] [14] [15]

2. Pièges liés à l'utilisation des fermetures

In [1]: def create():
   ...:     return [lambda x:i*x for i in range(5)]  #推导式生成一个匿名函数的列表
   ...: 

In [2]: create()Out[2]: 
[<function __main__.<lambda>>,
 <function __main__.<lambda>>,
 <function __main__.<lambda>>,
 <function __main__.<lambda>>,
 <function __main__.<lambda>>]In [4]: for mul in create():
   ...:     print mul(2)
   ...:     
88888

Le résultat n'est-il pas très étrange ? C'est un piège dans l'utilisation des fermetures ! Voyons pourquoi ?

Dans le code ci-dessus, la fonction create renvoie un list qui contient 4 variables de fonction. Ces quatre fonctions font toutes référence à la variable de boucle i, ce qui signifie qu'elles partagent les mêmes variables i. et i changera lorsque la fonction est appelée, la variable de boucle i est déjà égale à 4, donc les quatre fonctions renvoient 8. Si vous devez utiliser la valeur d'une variable de boucle dans une fermeture, utilisez la variable de boucle comme paramètre par défaut de la fermeture ou implémentez-la via une fonction partielle. Le principe de mise en œuvre est également très simple, c'est-à-dire que lorsque la variable de boucle est passée dans la fonction en tant que paramètre, une nouvelle mémoire sera demandée. L'exemple de code est le suivant :

In [5]: def create():
   ...:         return [lambda x,i=i:i*x for i in range(5)] 
   ...: 
In [7]: for mul in create():
   ...:     print mul(2)
   ...:     
02468

3. Fermetures et décorateurs

Le décorateur est une application de fermeture, mais il transmet une fonction :

def addb(func):def wrapper():return &#39;<b>&#39; + func() + &#39;</b>&#39;return wrapperdef addli(func):def wrapper():return &#39;<li>&#39; + func() + &#39;</li>&#39;return wrapper 

@addb         # 等同于 demo = addb(addli(demo)) 
@addli        # 等同于 demo = addli(demo)def demo():return &#39;hello world&#39;

print demo()    # 执行的是 addb(addku(demo))

Lors de l'exécution, transmettez d'abord la fonction demo à addli pour la décoration, puis transmettez la fonction décorée à addb pour la décoration. Le résultat final renvoyé est donc :

<b><li>hello world</li></b>

4. Pièges dans les décorateurs

Lorsque vous écrivez un décorateur qui agit sur une fonction, les méta-informations importantes de cette fonction Par exemple, les noms, les docstrings, les annotations et les signatures de paramètres seront perdus.

def out_func(func):def wrapper():
        func()return wrapper@out_funcdef demo():"""
        this is  a demo.
    """print &#39;hello world.&#39;if __name__ == &#39;__main__&#39;:
    demo()print "__name__:",demo.__name__print "__doc__:",demo.__doc__

Regardez les résultats :

hello world.__name__: wrapper__doc__: None

Le nom de la fonction et la chaîne de documentation sont devenus des informations de fermeture. Heureusement, vous pouvez utiliser le décorateur functools dans la bibliothèque pour annoter la fonction wrapper sous-jacente. @wraps

from functools import wrapsdef out_func(func):    @wraps(func)def wrapper():
        func()return wrapper
Essayez les résultats par vous-même !

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn