is
vérifie l'identité des objets, et toute implémentation de Python, lorsqu'elle rencontre le littéral des types immuables, est parfaitement libre de soit créer un nouvel objet de ce type immuable, o rechercher les objets existants de ce type pour voir si certains d'entre eux peuvent être réutilisés (en ajoutant une nouvelle référence au même objet sous-jacent). Il s'agit d'un choix pragmatique d'optimisation et de pas soumis à des contraintes sémantiques, de sorte que votre code ne doit jamais dépendre du chemin que peut prendre une implémentation donnée (ou il pourrait se briser avec une version de Python corrigée ou optimisée !)
Prenons un exemple :
>>> import dis
>>> def f():
... x = 'google.com'
... return x is 'google.com'
...
>>> dis.dis(f)
2 0 LOAD_CONST 1 ('google.com')
3 STORE_FAST 0 (x)
3 6 LOAD_FAST 0 (x)
9 LOAD_CONST 1 ('google.com')
12 COMPARE_OP 8 (is)
15 RETURN_VALUE
dans cette mise en œuvre particulière, à l'intérieur d'une fonction votre observation ne s'applique pas et un seul objet est fait pour le littéral (tout littéral), et, en effet :
>>> f()
True
D'un point de vue pragmatique, c'est parce qu'à l'intérieur d'une fonction, faire un passage par la table locale des constantes (pour économiser de la mémoire en ne créant pas de multiples objets immuables constants là où un seul suffit) est plutôt bon marché et rapide, et peut offrir de bons rendements de performance puisque la fonction peut être appelée de façon répétée par la suite.
Mais, la même mise en œuvre, à l'invite interactive ( Editar : Je pensais initialement que cela se produirait également au niveau supérieur d'un module, mais un commentaire de @Thomas m'a remis dans le droit chemin, voir plus loin) :
>>> x = 'google.com'
>>> y = 'google.com'
>>> id(x), id(y)
(4213000, 4290864)
ne prend PAS la peine d'essayer d'économiser de la mémoire de cette manière -- la id
sont différents, c'est-à-dire des objets distincts. Il y a potentiellement des coûts plus élevés et des rendements plus faibles et donc l'heuristique de l'optimiseur de cette implémentation lui dit de ne pas s'embêter à chercher et de continuer.
Editar : au niveau du module supérieur, selon l'observation de @Thomas, donné par exemple :
$ cat aaa.py
x = 'google.com'
y = 'google.com'
print id(x), id(y)
Nous voyons à nouveau l'optimisation de la mémoire basée sur la table des constantes dans cette implémentation :
>>> import aaa
4291104 4291104
(fin de l'édition selon l'observation de @Thomas).
Enfin, toujours sur la même mise en œuvre :
>>> x = 'google'
>>> y = 'google'
>>> id(x), id(y)
(2484672, 2484672)
l'heuristique est différente ici parce que la chaîne littérale "ressemble à un identifiant" -- donc elle pourrait être utilisée dans une opération nécessitant un internage... donc l'optimiseur l'internalise de toute façon (et une fois internée, sa recherche devient très rapide bien sûr). Et en effet, surprise surprise.. :
>>> z = intern(x)
>>> id(z)
2484672
... x
a a été intern
pour la toute première fois (comme vous le voyez, la valeur de retour de la commande intern
es le même en tant qu'objet x
y y
car il a la même id()
). Bien sûr, vous ne devriez pas non plus vous fier à cela - l'optimiseur ne ont d'internaliser quoi que ce soit automatiquement, il s'agit juste d'une heuristique d'optimisation. intern
ed string, intern
explicitement, juste pour être sûr. Lorsque vous hacer internaliser les chaînes de caractères de manière explicite... :
>>> x = intern('google.com')
>>> y = intern('google.com')
>>> id(x), id(y)
(4213000, 4213000)
...alors vous hacer assurer exactement le même objet (c'est-à-dire le même id()
) à chaque fois, ce qui vous permet d'appliquer des micro-optimisations telles que la vérification de l'utilisation de la fonction is
plutôt que ==
(Je n'ai jamais trouvé que le gain de performance minuscule en valait la peine;-).
Editar Pour clarifier, voici le genre de différences de performances dont je parle, sur un Macbook Air lent... :
$ python -mtimeit -s"a='google';b='google'" 'a==b'
10000000 loops, best of 3: 0.132 usec per loop
$ python -mtimeit -s"a='google';b='google'" 'a is b'
10000000 loops, best of 3: 0.107 usec per loop
$ python -mtimeit -s"a='goo.gle';b='goo.gle'" 'a==b'
10000000 loops, best of 3: 0.132 usec per loop
$ python -mtimeit -s"a='google';b='google'" 'a is b'
10000000 loops, best of 3: 0.106 usec per loop
$ python -mtimeit -s"a=intern('goo.gle');b=intern('goo.gle')" 'a is b'
10000000 loops, best of 3: 0.0966 usec per loop
$ python -mtimeit -s"a=intern('goo.gle');b=intern('goo.gle')" 'a == b'
10000000 loops, best of 3: 0.126 usec per loop
...quelques dizaines de nanosecondes dans les deux sens, tout au plus. Donc, valant même réflexion seulement dans les situations les plus extrêmes "optimiser le [expletive deleted] de ce [expletive deleted] goulot d'étranglement de performance"!-)