web-dev-qa-db-fra.com

Pourquoi (0-6) est -6 = Faux?

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'>
>>> 
130
Dmitry Zagorulkin

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 ==.

151
kennytm

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 )

29
Simeon Visser

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.

26
Russell Borogove

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

17
Ashwini Chaudhary