私は遅れていますが、あなたはあなたの答えでいくつかの情報源が欲しいですか 私はこれを紹介的な方法で試してみて、より多くの人々がフォローできるようにします。
CPythonの良いところは、このソースを実際に確認できることです。3.5リリースではリンクを使用しますが、対応する2.xのリンクを見つけるのは簡単です。
CPythonでは、新しいオブジェクトの作成を処理するC-API関数int
はPyLong_FromLong(long v)
です。この関数の説明は次のとおりです。
現在の実装では、-5〜256のすべての整数の整数オブジェクトの配列が保持されます。その範囲でintを作成すると、実際には既存のオブジェクトへの参照が返されます。したがって、値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)
call get_small_int
です。
次に、そのすべての栄光を見てみましょうget_small_int
を見てみましょう(まあ、それは興味深いものがある場所なので、本体だけを見ていきます)。
PyObject *v;
assert(-NSMALLNEGINTS <= ival && ival < NSMALLPOSINTS);
v = (PyObject *)&small_ints[ival + NSMALLNEGINTS];
Py_INCREF(v);
さて、を宣言PyObject
し、前の条件が満たされていることを表明して、割り当てを実行します。
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
上でIDを確認すると、まったく同じものが返されます。
しかし、いつ割り当てられますか?
_PyLong_Init
Pythonでの初期化中に、喜んで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
ます。