Je suis en retard mais, vous voulez une source avec votre réponse? Je vais essayer de formuler ceci de manière introductive afin que plus de gens puissent suivre.
Une bonne chose à propos de CPython est que vous pouvez réellement voir la source de cela. Je vais utiliser des liens pour la version 3.5 , mais trouver le 2.x correspondant ceux qui est trivial.
Dans CPython, la fonction C-API qui gère la création d'un nouvel intobjet est PyLong_FromLong(long v). La description de cette fonction est:
L'implémentation actuelle conserve un tableau d'objets entiers pour tous les entiers compris entre -5 et 256, lorsque vous créez un int dans cette plage, vous récupérez simplement une référence à l'objet existant . Il devrait donc être possible de changer la valeur de 1. Je soupçonne que le comportement de Python dans ce cas n'est pas défini. :-)
(Mes italiques)
Je ne sais pas pour vous mais je vois cela et je pense: Trouvons ce tableau!
Si vous n'avez pas manipulé le code C implémentant CPython, vous devriez ; tout est assez organisé et lisible. Pour notre cas, nous devons regarder dans le Objectssous - répertoire de l' arborescence du répertoire du code source principal .
PyLong_FromLongtraite des longobjets, il ne devrait donc pas être difficile de déduire que nous devons jeter un œil à l'intérieur longobject.c. Après avoir regardé à l'intérieur, vous pourriez penser que les choses sont chaotiques; ils le sont, mais n'ayez crainte, la fonction que nous recherchons est de se détendre à la ligne 230 en attendant que nous le vérifions. C'est une fonction assez petite, donc le corps principal (à l'exclusion des déclarations) est facilement collé ici:
PyObject *
PyLong_FromLong(long ival)
{
// omitting declarations
CHECK_SMALL_INT(ival);
if (ival < 0) {
/* negate: cant write this as abs_ival = -ival since that
invokes undefined behaviour when ival is LONG_MIN */
abs_ival = 0U-(unsigned long)ival;
sign = -1;
}
else {
abs_ival = (unsigned long)ival;
}
/* Fast path for single-digit ints */
if (!(abs_ival >> PyLong_SHIFT)) {
v = _PyLong_New(1);
if (v) {
Py_SIZE(v) = sign;
v->ob_digit[0] = Py_SAFE_DOWNCAST(
abs_ival, unsigned long, digit);
}
return (PyObject*)v;
}
Maintenant, nous ne sommes pas un C master-code-haxxorz mais nous ne sommes pas non plus stupides, nous pouvons voir que CHECK_SMALL_INT(ival);nous jeter un œil séduisant; nous pouvons comprendre que cela a quelque chose à voir avec cela. Regardons ça:
#define CHECK_SMALL_INT(ival) \
do if (-NSMALLNEGINTS <= ival && ival < NSMALLPOSINTS) { \
return get_small_int((sdigit)ival); \
} while(0)
C'est donc une macro qui appelle la fonction get_small_intsi la valeur ivalsatisfait la condition:
if (-NSMALLNEGINTS <= ival && ival < NSMALLPOSINTS)
Alors, quels sont NSMALLNEGINTSet NSMALLPOSINTS? Macros! Les voici :
#ifndef NSMALLPOSINTS
#define NSMALLPOSINTS 257
#endif
#ifndef NSMALLNEGINTS
#define NSMALLNEGINTS 5
#endif
Notre condition est donc l' if (-5 <= ival && ival < 257)appel get_small_int.
Ensuite, regardons get_small_intdans toute sa splendeur (eh bien, nous allons simplement regarder son corps parce que c'est là que les choses intéressantes sont):
PyObject *v;
assert(-NSMALLNEGINTS <= ival && ival < NSMALLPOSINTS);
v = (PyObject *)&small_ints[ival + NSMALLNEGINTS];
Py_INCREF(v);
D'accord, déclarez a PyObject, affirmez que la condition précédente est vérifiée et exécutez l'affectation:
v = (PyObject *)&small_ints[ival + NSMALLNEGINTS];
small_intsressemble beaucoup à ce tableau que nous recherchions, et il l'est! Nous aurions pu lire la fichue documentation et nous l'aurions su tout au long! :
/* Small integers are preallocated in this array so that they
can be shared.
The integers that are preallocated are those in the range
-NSMALLNEGINTS (inclusive) to NSMALLPOSINTS (not inclusive).
*/
static PyLongObject small_ints[NSMALLNEGINTS + NSMALLPOSINTS];
Alors ouais, c'est notre gars. Lorsque vous souhaitez créer un nouveau intdans la plage, [NSMALLNEGINTS, NSMALLPOSINTS)vous récupérez simplement une référence à un objet déjà existant qui a été préalloué.
Étant donné que la référence se réfère au même objet, délivrer id()directement ou vérifier l'identité avecis dessus retournera exactement la même chose.
Mais, quand sont-ils alloués ??
Lors de l'initialisation en_PyLong_Init Python, il vous fera plaisir d'entrer dans une boucle for, faites-le pour vous:
for (ival = -NSMALLNEGINTS; ival < NSMALLPOSINTS; ival++, v++) {
Consultez la source pour lire le corps de la boucle!
J'espère que mon explication vous a fait des choses C clairement maintenant (jeu de mots évidemment intentionnel).
Mais 257 is 257,? Quoi de neuf?
C'est en fait plus facile à expliquer, et j'ai déjà essayé de le faire ; c'est dû au fait que Python exécutera cette instruction interactive comme un seul bloc:
>>> 257 is 257
Lors de la compilation de cette instruction, CPython verra que vous avez deux littéraux correspondants et utilisera le même PyLongObjectreprésentant 257. Vous pouvez le voir si vous faites vous-même la compilation et examinez son contenu:
>>> codeObj = compile("257 is 257", "blah!", "exec")
>>> codeObj.co_consts
(257, None)
Lorsque CPython effectue l'opération, il va maintenant simplement charger exactement le même objet:
>>> import dis
>>> dis.dis(codeObj)
1 0 LOAD_CONST 0 (257) # dis
3 LOAD_CONST 0 (257) # dis again
6 COMPARE_OP 8 (is)
Ainsi isreviendra True.