Ich bin spät dran, aber Sie möchten eine Quelle mit Ihrer Antwort? Ich werde versuchen, dies einleitend zu formulieren, damit weitere Leute folgen können.
Eine gute Sache bei CPython ist, dass Sie tatsächlich die Quelle dafür sehen können. Ich werde Links für die Version 3.5 verwenden , aber die entsprechenden 2.x- Links zu finden ist trivial.
In CPython lautet die C-API- Funktion zum Erstellen eines neuen int
Objekts PyLong_FromLong(long v)
. Die Beschreibung für diese Funktion lautet:
Die aktuelle Implementierung behält ein Array von Ganzzahlobjekten für alle Ganzzahlen zwischen -5 und 256 bei. Wenn Sie ein Int in diesem Bereich erstellen, erhalten Sie tatsächlich nur einen Verweis auf das vorhandene Objekt zurück . Es sollte also möglich sein, den Wert 1 zu ändern. Ich vermute, dass das Verhalten von Python in diesem Fall undefiniert ist. :-)
(Meine Kursivschrift)
Ich weiß nichts über dich, aber ich sehe das und denke: Lass uns das Array finden!
Wenn Sie nicht mit dem C-Code herumgespielt haben, der CPython implementiert , sollten Sie dies tun . alles ist ziemlich gut organisiert und lesbar. Für unseren Fall müssen wir im Objects
Unterverzeichnis des Hauptquellcode-Verzeichnisbaums suchen .
PyLong_FromLong
befasst sich mit long
Objekten, daher sollte es nicht schwer sein, daraus zu schließen, dass wir einen Blick hineinwerfen müssen longobject.c
. Wenn Sie nach innen schauen, denken Sie vielleicht, dass die Dinge chaotisch sind. Sie sind, aber keine Angst, die Funktion, nach der wir suchen, ist, in Zeile 230 zu chillen und darauf zu warten, dass wir sie überprüfen. Da es sich um eine kleinere Funktion handelt, kann der Hauptteil (ohne Deklarationen) hier leicht eingefügt werden:
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;
}
Jetzt sind wir kein C- Master-Code-Haxxorz, aber wir sind auch nicht dumm. Wir können sehen, dass CHECK_SMALL_INT(ival);
wir alle verführerisch angesehen werden. wir können verstehen, dass es etwas damit zu tun hat. Schauen wir es uns an:
#define CHECK_SMALL_INT(ival) \
do if (-NSMALLNEGINTS <= ival && ival < NSMALLPOSINTS) { \
return get_small_int((sdigit)ival); \
} while(0)
Es ist also ein Makro, das die Funktion aufruft, get_small_int
wenn der Wert ival
die Bedingung erfüllt:
if (-NSMALLNEGINTS <= ival && ival < NSMALLPOSINTS)
Also was sind NSMALLNEGINTS
und NSMALLPOSINTS
? Makros! Hier sind sie :
#ifndef NSMALLPOSINTS
#define NSMALLPOSINTS 257
#endif
#ifndef NSMALLNEGINTS
#define NSMALLNEGINTS 5
#endif
Unser Zustand ist also if (-5 <= ival && ival < 257)
Anruf get_small_int
.
Als nächstes schauen wir uns get_small_int
seine ganze Pracht an (nun, wir schauen uns nur seinen Körper an, denn dort sind die interessanten Dinge):
PyObject *v;
assert(-NSMALLNEGINTS <= ival && ival < NSMALLPOSINTS);
v = (PyObject *)&small_ints[ival + NSMALLNEGINTS];
Py_INCREF(v);
Okay, deklarieren Sie a PyObject
, behaupten Sie, dass die vorherige Bedingung gilt, und führen Sie die Zuweisung aus:
v = (PyObject *)&small_ints[ival + NSMALLNEGINTS];
small_ints
sieht dem Array, nach dem wir gesucht haben, sehr ähnlich, und das ist es auch! Wir hätten einfach die verdammte Dokumentation lesen können und wir hätten es die ganze Zeit gewusst! ::
/* 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];
Also ja, das ist unser Typ. Wenn Sie ein neues Objekt int
im Bereich erstellen möchten, erhalten [NSMALLNEGINTS, NSMALLPOSINTS)
Sie nur einen Verweis auf ein bereits vorhandenes Objekt zurück, das vorab zugewiesen wurde.
Da sich die Referenz auf dasselbe Objekt bezieht, wird durch id()
direktes Ausgeben oder Überprüfen der Identität mit is
genau dasselbe zurückgegeben.
Aber wann werden sie zugeteilt?
Während der Initialisierung in_PyLong_Init
Python wird gerne eine for-Schleife eingegeben. Tun Sie dies für Sie:
for (ival = -NSMALLNEGINTS; ival < NSMALLPOSINTS; ival++, v++) {
Überprüfen Sie die Quelle, um den Schleifenkörper zu lesen!
Ich hoffe , meine Erklärung Sie gemacht hat C die Dinge klar jetzt (pun offensichtlich intented).
Aber 257 is 257
? Wie geht's?
Dies ist tatsächlich einfacher zu erklären, und ich habe bereits versucht, dies zu tun . Dies liegt an der Tatsache, dass Python diese interaktive Anweisung als einzelnen Block ausführt:
>>> 257 is 257
Während des Abschlusses dieser Anweisung stellt CPython fest, dass Sie zwei übereinstimmende Literale haben und dieselbe PyLongObject
Darstellung verwenden 257
. Sie können dies sehen, wenn Sie die Zusammenstellung selbst durchführen und deren Inhalt untersuchen:
>>> codeObj = compile("257 is 257", "blah!", "exec")
>>> codeObj.co_consts
(257, None)
Wenn CPython die Operation ausführt, wird jetzt genau dasselbe Objekt geladen:
>>> import dis
>>> dis.dis(codeObj)
1 0 LOAD_CONST 0 (257) # dis
3 LOAD_CONST 0 (257) # dis again
6 COMPARE_OP 8 (is)
Also is
werde ich zurückkehren True
.