遅くなりましたが、回答の情報源が必要ですか? より多くの人が従うことができるように、これを紹介的な方法で表現しようとします.
CPython の良いところは、このソースを実際に見ることができることです。3.5リリースのリンクを使用しますが、対応する2.xのリンクを見つけるのは簡単です。
CPython では、新しいオブジェクトの作成を処理するC-API関数は. この関数の説明は次のとおりです。int
PyLong_FromLong(long v)
現在の実装では、-5 から 256 までのすべての整数に対して整数オブジェクトの配列が保持されます。その範囲で int を作成すると、実際には既存の object への参照が返されます。したがって、1 の値を変更できるはずです。この場合の Python の動作は未定義であると思われます。:-)
(イタリック体)
あなたのことは知りませんが、私はこれを見て考えました:その配列を見つけよう!
CPythonを実装する C コードをいじっていない場合は、次のようにする必要があります。すべてがかなり整理されていて読みやすいです。この場合、メイン ソース コード ディレクトリ ツリーのObjects
サブディレクトリを調べる必要があります。
PyLong_FromLong
オブジェクトを扱うlong
ので、内部をのぞく必要があると推測するのは難しくありませんlongobject.c
。内部を見た後、物事は混沌としていると思うかもしれません。それらはそうですが、恐れることはありません。私たちが探している関数は、230 行目で身も凍るような状態で、チェックアウトするのを待っています。これは小さめの関数なので、本体 (宣言を除く) はここに簡単に貼り付けられます。
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;
}
さて、私たちは Cマスター コード haxxorzではありませんが、愚かでもありませんCHECK_SMALL_INT(ival);
。これと関係があることは理解できます。それをチェックしよう:
#define CHECK_SMALL_INT(ival) \
do if (-NSMALLNEGINTS <= ival && ival < NSMALLPOSINTS) { \
return get_small_int((sdigit)ival); \
} while(0)
get_small_int
したがって、値ival
が条件を満たす場合に関数を呼び出すマクロです。
if (-NSMALLNEGINTS <= ival && ival < NSMALLPOSINTS)
NSMALLNEGINTS
とは何NSMALLPOSINTS
ですか?マクロ!ここにそれらがあります:
#ifndef NSMALLPOSINTS
#define NSMALLPOSINTS 257
#endif
#ifndef NSMALLNEGINTS
#define NSMALLNEGINTS 5
#endif
したがって、条件はif (-5 <= ival && ival < 257)
callget_small_int
です。
次に、get_small_int
そのすべての栄光を見てみましょう (まあ、興味深いのはその本体だけです)。
PyObject *v;
assert(-NSMALLNEGINTS <= ival && ival < NSMALLPOSINTS);
v = (PyObject *)&small_ints[ival + NSMALLNEGINTS];
Py_INCREF(v);
よし、 aPyObject
を宣言し、前の条件が保持されていることをアサートし、代入を実行します。
v = (PyObject *)&small_ints[ival + NSMALLNEGINTS];
small_ints
これは、私たちが探し求めてきた配列に非常によく似ています。いまいましいドキュメントを読むだけで、ずっとわかっていたはずです! :
/* 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];
そうです、これは私たちの男です。int
範囲内に新しいオブジェクトを作成する場合[NSMALLNEGINTS, NSMALLPOSINTS)
は、事前に割り当てられた既存のオブジェクトへの参照を取得するだけです。
参照は同じオブジェクトを参照するため、id()
直接発行するか、同一性をチェックするとis
、まったく同じものが返されます。
しかし、いつ割り当てられますか??
Python での初期化中_PyLong_Init
に、喜んで for ループに入り、これを行います。
for (ival = -NSMALLNEGINTS; ival < NSMALLPOSINTS; ival++, v++) {
ループ本体を読むためにソースをチェックしてください!
私の説明で、あなたがCのことを明確に理解できたことを願っています (しゃれは明らかに意図されています)。
しかし、257 is 257
?調子はどう?
これは実際には説明が簡単で、私はすでにそうしようとしました。これは、Python がこのインタラクティブなステートメントを単一のブロックとして実行するためです。
>>> 257 is 257
このステートメントのコンパイル中に、CPython は 2 つの一致するリテラルがあることを認識し、同じPyLongObject
表現を使用し257
ます。これは、自分でコンパイルしてその内容を調べるとわかります。
>>> codeObj = compile("257 is 257", "blah!", "exec")
>>> codeObj.co_consts
(257, None)
CPython が操作を実行すると、まったく同じオブジェクトが読み込まれます。
>>> import dis
>>> dis.dis(codeObj)
1 0 LOAD_CONST 0 (257) # dis
3 LOAD_CONST 0 (257) # dis again
6 COMPARE_OP 8 (is)
だからis
戻ってきTrue
ます。