Duplicata possible:
L'opérateur "is" de Python se comporte de façon inattendue avec des entiers
Aujourd'hui, j'ai essayé de déboguer mon projet et après quelques heures d'analyse, j'ai obtenu ceci:
>>> (0-6) is -6
False
mais,
>>> (0-5) is -5
True
Pourriez-vous m'expliquer pourquoi? C'est peut-être une sorte de bogue ou un comportement très étrange.
> Python 2.7.3 (default, Apr 24 2012, 00:00:54) [GCC 4.7.0 20120414 (prerelease)] on linux2
>>> type(0-6)
<type 'int'>
>>> type(-6)
<type 'int'>
>>> type((0-6) is -6)
<type 'bool'>
>>>
Tous les entiers de -5 à 256 inclus sont mis en cache en tant qu'objets globaux partageant la même adresse avec CPython, ainsi le test is
réussit.
Cet artefact est expliqué en détail dans http://www.laurentluce.com/posts/python-integer-objects-implementation/ , et nous pourrions vérifier le code source actuel dans http: //hg.python.org/cpython/file/tip/Objects/longobject.c .
Une structure spécifique est utilisée pour référencer les petits entiers et les partager afin que l'accès soit rapide. Il s'agit d'un tableau de 262 pointeurs vers des objets entiers. Ces objets entiers sont alloués lors de l'initialisation dans un bloc d'objets entiers que nous avons vu ci-dessus. La plage des petits entiers va de -5 à 256. De nombreux programmes Python passent beaucoup de temps à utiliser des entiers dans cette plage, c'est donc une décision intelligente.
Ceci n'est qu'un détail d'implémentation de CPython et vous ne devriez pas vous y fier. Par exemple, PyPy a implémenté le id
d'entier pour se retourner, donc (0-6) is -6
est toujours vrai même si ce sont des "objets différents" en interne; il vous permet également de configurer l'activation de cette mise en cache d'entiers et même de définir les limites inférieure et supérieure. Mais en général, les objets récupérés d'origines différentes ne seront pas identiques. Si vous voulez comparer l'égalité, utilisez simplement ==
.
Python stocke des entiers compris entre -5 et 256 dans l'interpréteur: il possède un pool d'objets entiers à partir desquels ces entiers sont renvoyés. Voilà pourquoi ces objets sont les mêmes: (0-5)
et -5
mais non (0-6)
et -6
car ceux-ci sont créés sur place.
Voici la source dans le code source de CPython:
#define NSMALLPOSINTS 257
#define NSMALLNEGINTS 5
static PyIntObject *small_ints[NSMALLNEGINTS + NSMALLPOSINTS];
( voir le code source CPython : /trunk/Objects/intobject.c
). Le code source inclut le commentaire suivant:
/* References to small integers are saved in this array so that they
can be shared.
The integers that are saved are those in the range
-NSMALLNEGINTS (inclusive) to NSMALLPOSINTS (not inclusive).
*/
L'opérateur is
les compare ensuite (-5
) comme égaux car ils sont le même objet (même emplacement mémoire) mais les deux autres nouveaux entiers (-6
) sera à différents emplacements de mémoire (puis is
ne renverra pas True
). Notez que 257
dans le code source ci-dessus est pour les entiers positifs de sorte que 0 - 256
(inclus).
( source )
Ce n'est pas un bug. is
n'est pas un test d'égalité. ==
donnera les résultats attendus.
La raison technique de ce comportement est qu'une implémentation Python est libre de traiter différentes instances de la même valeur constante comme le même objet ou comme des objets différents. Le Python l'implémentation que vous utilisez choisit de faire en sorte que certaines petites constantes partagent le même objet pour des raisons d'économie de mémoire. Vous ne pouvez pas vous fier à ce que ce comportement soit la même version à la version ou à travers différentes Python = implémentations.
Cela se produit parce que CPython met en cache quelques petits entiers et petites chaînes et donne à chaque instance de cet objet un même id()
.
(0-5)
Et -5
Ont la même valeur pour id()
, ce qui n'est pas vrai pour 0-6
Et -6
>>> id((0-6))
12064324
>>> id((-6))
12064276
>>> id((0-5))
10022392
>>> id((-5))
10022392
De même pour les chaînes:
>>> x = 'abc'
>>> y = 'abc'
>>> x is y
True
>>> x = 'a little big string'
>>> y = 'a little big string'
>>> x is y
False
Pour plus de détails sur la mise en cache des chaînes, lisez: L'opérateur is
se comporte différemment lors de la comparaison de chaînes avec des espaces