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

零下一度
Libérer: 2017-06-25 09:55:03
original
1416 Les gens l'ont consulté

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
Copier après la connexion

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
Copier après la connexion

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]
Copier après la connexion

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
Copier après la connexion

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
Copier après la connexion

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))
Copier après la connexion

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>
Copier après la connexion

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__
Copier après la connexion

Regardez les résultats :

hello world.__name__: wrapper__doc__: None
Copier après la connexion

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
Copier après la connexion
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!

Étiquettes associées:
source:php.cn
Déclaration de ce site Web
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
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal