613 votes

L'opérateur "is" se comporte de manière inattendue avec les entiers

Pourquoi l'élément suivant se comporte-t-il de manière inattendue en Python ?

>>> a = 256
>>> b = 256
>>> a is b
True           # This is an expected result
>>> a = 257
>>> b = 257
>>> a is b
False          # What happened here? Why is this False?
>>> 257 is 257
True           # Yet the literal numbers compare properly

J'utilise Python 2.5.2. En essayant différentes versions de Python, il semble que Python 2.3.3 présente le comportement ci-dessus entre 99 et 100.

Sur la base de ce qui précède, je peux émettre l'hypothèse que Python est implémenté en interne de telle sorte que les "petits" entiers sont stockés d'une manière différente de celle des grands entiers et que l'option de stockage de Python n'est pas utilisée. is L'opérateur peut faire la différence. Pourquoi l'abstraction fuyante ? Quelle est la meilleure façon de comparer deux objets arbitraires pour voir s'ils sont identiques lorsque je ne sais pas à l'avance s'il s'agit de nombres ou non ?

2 votes

Jetez un coup d'œil aquí > L'implémentation actuelle conserve un tableau d'objets entiers pour tous les > entiers compris entre -5 et 256, lorsque vous créez un entier dans cette plage, vous > récupérez simplement une référence à l'objet existant.

3 votes

Il s'agit d'un détail d'implémentation spécifique à CPython et d'un comportement non défini, à utiliser avec précaution.

0 votes

Cela répond-il à votre question ? Y a-t-il une différence entre "==" et "est" ?

454voto

Cybis Points 5062

Jetez un coup d'oeil à ça :

>>> a = 256
>>> b = 256
>>> id(a)
9987148
>>> id(b)
9987148
>>> a = 257
>>> b = 257
>>> id(a)
11662816
>>> id(b)
11662828

Voici ce que j'ai trouvé dans la documentation de Python 2, "Plain Integer Objects" (C'est la même chose pour Python 3 ) :

L'implémentation actuelle conserve un tableau d'objets entiers pour tous les entiers compris entre -5 et 256, lorsque vous créez un entier dans cette plage, vous vous récupérez juste une référence à l'objet existant. Donc ça devrait être possible de changer la valeur de 1. suspecter que le comportement de Python dans dans ce cas est indéfini :-)

62 votes

Quelqu'un sait-il comment cette plage (-5, 256) a été choisie ? je ne serais pas trop surpris si c'était (0, 255) ou même (-255, 255), mais une plage de 262 nombres commençant à -5 semble étonnamment arbitraire.

8 votes

@WoodrowBarlow : Le -5 est juste une heuristique pour capturer les placeholders négatifs communs, je pense. 0..255 couvre les tableaux de valeurs d'un seul octet. C'est 256 qui est mystérieux, mais je suppose que c'est pour (dés)assembler des entiers en/depuis des octets.

4 votes

D'après ce que j'ai compris, la fourchette a été choisie en examinant les valeurs couramment utilisées dans plusieurs projets (et dans plusieurs langues).

63voto

JimB Points 9246

Cela dépend si vous cherchez à savoir si deux choses sont égales ou si c'est le même objet.

is vérifie s'il s'agit du même objet, et pas seulement d'un objet égal. Les petits ints pointent probablement vers le même emplacement mémoire pour des raisons d'espace.

In [29]: a = 3
In [30]: b = 3
In [31]: id(a)
Out[31]: 500729144
In [32]: id(b)
Out[32]: 500729144

Vous devez utiliser == pour comparer l'égalité d'objets arbitraires. Vous pouvez spécifier le comportement avec l'option __eq__ y __ne__ attributs.

0 votes

Bravo pour avoir expliqué comment comparer des objets arbitraires, comme le demande le PO !

39voto

Angel Points 616

Comme vous pouvez le vérifier fichier source intobject.c Python met en cache les petits entiers pour des raisons d'efficacité. Chaque fois que vous créez une référence à un petit nombre entier, vous faites référence au petit nombre entier mis en cache, et non à un nouvel objet. 257 n'est pas un petit nombre entier, il est donc calculé comme un objet différent.

Il est préférable d'utiliser == à cette fin.

22voto

Amit Points 503

Je pense que votre hypothèse est correcte. Faites une expérience avec id (identité de l'objet) :

In [1]: id(255)
Out[1]: 146349024

In [2]: id(255)
Out[2]: 146349024

In [3]: id(257)
Out[3]: 146802752

In [4]: id(257)
Out[4]: 148993740

In [5]: a=255

In [6]: b=255

In [7]: c=257

In [8]: d=257

In [9]: id(a), id(b), id(c), id(d)
Out[9]: (146349024, 146349024, 146783024, 146804020)

Il semble que les chiffres <= 255 sont traités comme des littéraux et tout ce qui est au-dessus est traité différemment !

2 votes

C'est parce que les objets représentant les valeurs de -5 à +256 sont créés au moment du démarrage - et donc toute utilisation de ces valeurs fait appel à un objet pré-construit. Presque toutes les références à des entiers en dehors de cette plage créent un nouvel objet interne à chaque fois qu'ils sont référencés. Je pense que l'utilisation du terme littéral prête à confusion - le littéral se réfère normalement à toute valeur qui est tapée dans un morceau de code - donc tous les nombres dans le code source sont des littéraux.

16voto

thereisnospork Points 169

L'opérateur "is" n'est pas une autre façon de taper "==".

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X