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
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]
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
Le décorateur est une application de fermeture, mais il transmet une fonction :
def addb(func):def wrapper():return '<b>' + func() + '</b>'return wrapperdef addli(func):def wrapper():return '<li>' + func() + '</li>'return wrapper @addb # 等同于 demo = addb(addli(demo)) @addli # 等同于 demo = addli(demo)def demo():return 'hello world' 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>
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 'hello world.'if __name__ == '__main__': 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
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!