Python >> Tutoriel Python >  >> Python

Mise en cache de petits entiers Python :==versus is

Cet extrait de code intéressant a été porté à mon attention par le lecteur Finxter Albrecht.

a, b = 250, 250
for i in range(250, 260):
    if a is not b:
        break
    a += 1
    b += 1
print(a)
# What's the output of this code snippet?

Vous devinez que la boucle for va de i=250 à i=259 , en incrémentant à chaque fois a et b . Comme Python crée un objet entier auquel les deux noms se réfèrent, la commande a is not b doit toujours être False . Ainsi, le résultat est a=259 , n'est-ce pas ?

FAUX !!! $%&&%$

Exercice :Exécutez le code et vérifiez le résultat. Vous vous attendiez à ça ?

Le résultat est a=257 .

La raison en est un détail d'implémentation de l'implémentation CPython appelé "Small Integer Caching" - le cache interne des entiers en Python.

Si vous créez un objet entier compris entre -5 et 256, Python ne renverra qu'une référence à cet objet - qui est déjà mis en cache en mémoire.

Vous pouvez visualiser l'exécution du code dans ce visualiseur de mémoire interactif :

Exercice :Cliquez sur suivant jusqu'à ce que vous voyiez le résultat. Combien y a-t-il d'entiers en mémoire ?

Examinons rapidement la signification de "est" en Python.

L'opérateur est

L'opérateur is vérifie si deux noms de variables pointent vers le même objet en mémoire :

>>> a = "hello"
>>> b = "hello"
>>> a is b
True

Les deux variables a et b pointe vers la chaîne "hello" . Python ne stocke pas deux fois la même chaîne mais ne la crée qu'une seule fois en mémoire. Cela économise de la mémoire et rend Python plus rapide et plus efficace. Et ce n'est pas un problème car les chaînes sont immuables - donc une variable ne peut pas "éclipser" un objet chaîne d'une autre variable.

Notez que nous pouvons utiliser le id() fonction pour vérifier une représentation entière de l'adresse mémoire :

>>> a = "hello"
>>> b = "hello"
>>> id(a)
1505840752992
>>> id(b)
1505840752992

Ils pointent tous les deux vers le même emplacement en mémoire ! Par conséquent, le is l'opérateur renvoie True !

Mise en cache des petits entiers

Encore une fois, si vous créez un objet entier compris entre -5 et 256, Python ne renverra qu'une référence à cet objet - qui est déjà mis en cache en mémoire. Mais si nous créons un objet entier qui ne tombe pas dans cette plage, Python peut renvoyer un nouvel objet entier avec la même valeur.

Si nous vérifions maintenant a is not b , Python nous donnera le résultat correct True .

En fait, cela conduit au comportement étrange de l'implémentation C de Python 3 :

>>> a = 256
>>> b = 256
>>> a is b
True
>>> a = 257
>>> b = 257
>>> a is b
False

Par conséquent, vous devez toujours comparer des entiers en utilisant le == opérateur en Python. Cela garantit que Python effectue une comparaison sémantique, et non une simple comparaison d'adresses mémoire :

>>> a = 256
>>> b = 256
>>> a == b
True
>>> a = 257
>>> b = 257
>>> a == b
True

Qu'est-ce que cela peut vous apprendre ? Les détails de mise en œuvre sont importants !