Maison > Article > développement back-end > Que sont l'is et l'identifiant apparaissant en Python ?
Premièrement, la fonction id peut obtenir l'adresse mémoire de l'objet. Si les adresses mémoire des deux objets sont les mêmes, alors les deux objets doivent être un seul objet. est équivalent à est. Code source Python comme preuve.
static PyObject * cmp_outcome(int op, register PyObject *v, register PyObject *w) { int res = 0; switch (op) { case PyCmp_IS: res = (v == w); break; case PyCmp_IS_NOT: res = (v != w); break;
Mais veuillez voir comment cette situation se produit dans le code ci-dessous ?
In [1]: def bar(self, x): ...: return self.x + y ...: In [2]: class Foo(object): ...: x = 9 ...: def __init__(self ,x): ...: self.x = x ...: bar = bar ...: In [3]: foo = Foo(5) In [4]: foo.bar is Foo.bar Out[4]: False In [5]: id(foo.bar) == id(Foo.bar) Out[5]: True
Deux objets sont jugés faux en utilisant is, mais sont vrais lorsqu'ils sont jugés en utilisant id. Ceci est incompatible avec les faits que nous connaissons. Comment expliquer ce phénomène ? La meilleure solution à cette situation est d'appeler le module dis pour voir ce que font les deux instructions de comparaison.
In [7]: dis.dis("id(foo.bar) == id(Foo.bar)") 0 BUILD_MAP 10340 3 BUILD_TUPLE 28527 6 <46> 7 DELETE_GLOBAL 29281 (29281) 10 STORE_SLICE+1 11 SLICE+2 12 DELETE_SUBSCR 13 DELETE_SUBSCR 14 SLICE+2 15 BUILD_MAP 10340 18 PRINT_EXPR 19 JUMP_IF_FALSE_OR_POP 11887 22 DELETE_GLOBAL 29281 (29281) 25 STORE_SLICE+1 In [8]: dis.dis("foo.bar is Foo.bar") 0 BUILD_TUPLE 28527 3 <46> 4 DELETE_GLOBAL 29281 (29281) 7 SLICE+2 8 BUILD_MAP 8307 11 PRINT_EXPR 12 JUMP_IF_FALSE_OR_POP 11887 15 DELETE_GLOBAL 29281 (29281)
La situation réelle est que lorsque l'opérateur . est exécuté, un objet proxy est réellement généré. Lorsque foo.bar est Foo.bar, deux objets sont générés séquentiellement et comparés sur la pile. Parce que les adresses sont différentes, elles doivent être fausses. mais c'est différent quand id(foo.bar) == id(Foo.bar). Tout d'abord, foo.bar est généré, puis l'adresse de foo.bar est calculée. Après avoir calculé l'adresse de foo.bar, il n'y en a pas. objet pointant vers foo .bar, donc l'objet foo.bar sera libéré. Générez ensuite l'objet Foo.bar. Puisque foo.bar et Foo.bar occupent la même taille de mémoire, l'adresse mémoire du foo.bar d'origine est réutilisée, donc id(foo.bar) == id(Foo. bar. ) est vrai.
Le contenu suivant est fourni par Leo Jay par e-mail. Il l'explique plus clairement.
L'idée d'utiliser id(expression a) == id(expression b) pour déterminer si les résultats de deux expressions sont le même objet est problématique.
Cette forme de foo.bar est appelée référence d'attribut [1], qui est un type d'expression. foo est un objet instance et bar est une méthode. À ce stade, le résultat renvoyé par l'expression foo.bar est appelé objet méthode [2]. D'après la documentation :
When an instance attribute is referenced that isn’t a data attribute, its class is searched. If the name denotes a valid class attribute that is a function object, a method object is created by packing (pointers to) the instance object and the function object just found together in an abstract object: this is the method object.
foo.bar lui-même n'est pas un simple nom, mais le résultat du calcul d'une expression, qui est un objet méthode. Dans une expression telle que id(foo.bar), l'objet méthode n'est qu'une variable intermédiaire temporaire. pour utiliser des variables comme identifiants.
Un exemple plus évident est
print id(foo.bar) == id(foo.__init__)
Le résultat de sortie est également vrai
Regardez la documentation de id[3] :
Return the “identity” of an object. This is an integer (or long integer) which is guaranteed to be unique and constant for this object during its lifetime. Two objects with non-overlapping lifetimes may have the same id() value. CPython implementation detail: This is the address of the object in memory.
Ce n'est que si vous pouvez garantir que l'objet ne sera pas détruit que vous pourrez utiliser id pour comparer deux objets. Alors, si vous insistez pour comparer, vous devez écrire ainsi :
fb = foo.bar Fb = Foo.bar print id(fb) == id(Fb)
Autrement dit, vous pouvez obtenir le résultat correct en liant les résultats des deux expressions aux noms, puis en comparant s'il s'agit du même objet.
La même chose est vraie pour l'expression is [4]. Le résultat correct que vous obtenez maintenant est entièrement dû aux détails d'implémentation actuels de CPython. L'implémentation actuelle consiste à calculer les objets sur les côtés gauche et droit, puis à comparer si les adresses des deux objets sont les mêmes. S'il est modifié un jour, calculez d'abord le côté gauche, enregistrez l'adresse, relâchez le côté gauche, puis calculez le côté droit et comparez à nouveau, le résultat de votre recherche peut être erroné. Ce problème est également mentionné dans la documentation officielle [5]. Je pense que la méthode correcte est comme l'identifiant, calculez d'abord les côtés gauche et droit, et liez-les explicitement à leurs noms respectifs, puis utilisez pour juger.
[1] http://docs.python.org/2/reference/expressions.html#attribute-references
[2] http://docs.python.org/2/tutorial/classes.html#method-objects
[3] http://docs.python.org/2/library/functions.html#id
[4] http://docs.python.org/2/reference/expressions.html#index-68
[5] http://docs.python.org/2/reference/expressions.html#id26
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!