Fermeture
En Python, une fonction est aussi un objet. Par conséquent, lorsque nous définissons une fonction, nous pouvons imbriquer une autre fonction et renvoyer la fonction imbriquée, par exemple :
from math import pow def make_pow(n): def inner_func(x): # 嵌套定义了 inner_func return pow(x, n) # 注意这里引用了外部函数的 n return inner_func # 返回 inner_func
Dans le code ci-dessus, la fonction make_pow définit une fonction interne inner_func , puis renvoie la fonction . On peut donc utiliser make_pow pour générer une autre fonction :
>> > pow2 = make_pow(2) # pow2 是一个函数,参数 2 是一个自由变量 >> > pow2 <function inner_func at 0x10271faa0 > >> > pow2(6) 36.0
Nous avons également remarqué que la fonction interne inner_func fait référence à la variable libre n de la fonction externe make_pow, ce qui signifie que lorsque la fonction make_pow dure après la le cycle se termine, la variable n sera toujours enregistrée dans inner_func, qui est référencée par inner_func.
>> > del make_pow # 删除 make_pow >> > pow3 = make_pow(3) Traceback(most recent call last): File "<stdin>", line 1, in < module > NameError: name 'make_pow' is not defined >> > pow2(9) # pow2 仍可正常调用,自由变量 2 仍保存在 pow2 中 81.0
---|---
Comme la situation ci-dessus, une fonction renvoie une fonction interne, qui fait référence aux paramètres et variables pertinents de la fonction externe que nous avons renvoyée. la fonction interne est appelée une fermeture.
Dans l'exemple ci-dessus, inner_func est une fermeture qui fait référence à la variable libre n.
Le rôle de la fermeture
La plus grande caractéristique de la fermeture est qu'elle fait référence à des variables libres Même si l'environnement qui a généré la fermeture a été libéré, la fermeture existe toujours. ;
Une fermeture peut avoir plusieurs instances au moment de l'exécution, même si les paramètres transmis sont les mêmes, par exemple :
>> > pow_a = make_pow(2) >> > pow_b = make_pow(2) >> > pow_a == pow_b False
Grâce aux fermetures, nous pouvons également simuler des instances de classes.
Construisez ici une classe pour trouver la distance d'un point à un autre :
from math import sqrt class Point(object): def __init__(self, x, y): self.x, self.y = x, y def get_distance(self, u, v): distance = sqrt((self.x - u) ** 2 + (self.y - v) ** 2) return distance >> > pt = Point(7, 2) # 创建一个点 >> > pt.get_distance(10, 6) # 求到另一个点的距离 5.0
Utilisez la fermeture pour mettre en œuvre :
def point(x, y): def get_distance(u, v): return sqrt((x - u) ** 2 + (y - v) ** 2) return get_distance >> > pt = point(7, 2) >> > pt(10, 6) 5.0
Comme vous pouvez le constater, le résultat est le même, mais l'utilisation de fermetures est plus concise que l'utilisation de classes.
Malentendus courants
Le concept de fermeture est très simple, mais sa mise en œuvre est sujette à certains malentendus, comme l'exemple suivant :
def count(): funcs = [] for i in [1, 2, 3]: def f(): return i funcs.append(f) return funcs
Dans cet exemple, we Une fonction est créée dans chaque boucle for et stockée dans funcs. Maintenant, lorsque vous appelez la fonction ci-dessus, vous pouvez penser que le résultat renvoyé est 1, 2, 3, mais en fait ce n'est pas le cas :
>> > f1, f2, f3 = count() >> > f1() 3 >> > f2() 3 >> > f3() 3
Pourquoi ? La raison en est que la fonction f ci-dessus fait référence à la variable i, mais la fonction f n'est pas exécutée immédiatement lorsque la boucle for se termine, la valeur de la variable i est 3 et les variables référencées par les fonctions dans funcs sont toutes 3. , et le résultat final est tous les 3.
Par conséquent, nous devrions essayer d'éviter de référencer des variables de boucle dans les fermetures, ou des variables qui changeront par la suite.
Comment résoudre la situation ci-dessus ? Nous pouvons créer une autre fonction et passer la valeur de la variable de boucle à la fonction, comme suit :
def count(): funcs = [] for i in [1, 2, 3]: def g(param): f = lambda: param # 这里创建了一个匿名函数 return f funcs.append(g(i)) # 将循环变量的值传给 g return funcs >> > f1, f2, f3 = count() >> > f1() 1 >> > f2() 2 >> > f3() 3
Résumé
Une fermeture est une fonction qui porte gratuitement variables , même si le cycle de vie de la fonction externe qui a créé la fermeture se termine, la variable libre référencée par la fermeture existera toujours.
Une fermeture peut avoir plusieurs instances en cours d'exécution.
Essayez de ne pas référencer des variables de boucle dans les fermetures, ou des variables qui changeront plus tard.
Ce qui précède est le contenu de Python avancé : état de fermeture. Pour plus de contenu connexe, veuillez faire attention au site Web PHP chinois (www.php.cn) !