ฉันสาย แต่คุณต้องการแหล่งที่มาพร้อมคำตอบของคุณหรือไม่ ฉันจะลองและคำนี้ในลักษณะเบื้องต้นเพื่อให้คนอื่น ๆ สามารถติดตามได้
สิ่งที่ดีเกี่ยวกับ CPython ก็คือคุณสามารถเห็นแหล่งที่มาของสิ่งนี้ได้ ฉันจะใช้ลิงค์สำหรับรุ่น3.5แต่การค้นหา2.xที่เกี่ยวข้องนั้นเป็นเรื่องเล็กน้อย
ใน CPython ที่C-APIฟังก์ชั่นที่จับสร้างใหม่วัตถุint
PyLong_FromLong(long v)
คำอธิบายสำหรับฟังก์ชั่นนี้คือ:
การดำเนินงานในปัจจุบันช่วยให้อาร์เรย์ของวัตถุจำนวนเต็มสำหรับจำนวนเต็มทั้งหมดระหว่าง -5 และ 256 เมื่อคุณสร้าง int ในช่วงที่คุณจริงเพียงแค่ได้รับกลับอ้างอิงถึงวัตถุที่มีอยู่ ดังนั้นจึงเป็นไปได้ที่จะเปลี่ยนค่า 1 ฉันสงสัยว่าพฤติกรรมของ Python ในกรณีนี้ไม่ได้กำหนดไว้ :-)
(ตัวเอียงของฉัน)
ไม่รู้เรื่องเกี่ยวกับคุณ แต่ฉันเห็นสิ่งนี้และคิดว่า: เรามาหาแถวนั้นกัน!
หากคุณยังไม่ได้เล่นกับรหัส C ที่ใช้ CPython คุณควร ; ทุกอย่างสวยจัดและอ่านได้ สำหรับกรณีของเราเราต้องดูใน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 master-code-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)
โทรget_small_int
การโทร
ต่อไปเรามาดูget_small_int
ในความรุ่งโรจน์ของมัน (เอาล่ะเราจะดูที่ร่างกายเพราะนั่นคือสิ่งที่น่าสนใจ):
PyObject *v;
assert(-NSMALLNEGINTS <= ival && ival < NSMALLPOSINTS);
v = (PyObject *)&small_ints[ival + NSMALLNEGINTS];
Py_INCREF(v);
โอเคประกาศ a 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
มันจะส่งคืนสิ่งเดียวกัน
แต่พวกเขาจะจัดสรรเมื่อไหร่ ??
ในระหว่างการเริ่มต้นใน_PyLong_Init
Python ยินดีที่จะใส่ในห่วงสำหรับทำเช่นนี้สำหรับคุณ:
for (ival = -NSMALLNEGINTS; ival < NSMALLPOSINTS; ival++, v++) {
ลองดูแหล่งที่มาเพื่ออ่านเนื้อความลูป!
ฉันหวังว่าคำอธิบายของฉันทำให้คุณชัดเจนในสิ่งCตอนนี้ (ปุนตั้งใจชัดเจน)
แต่, 257 is 257
? ว่าไง?
นี้เป็นจริงได้ง่ายขึ้นที่จะอธิบายและฉันมีความพยายามที่จะทำเช่นนั้นอยู่แล้ว ; เป็นเพราะข้อเท็จจริงที่ว่า Python จะดำเนินการคำสั่งแบบโต้ตอบนี้เป็นบล็อกเดียว:
>>> 257 is 257
ในช่วง complilation ของคำสั่งนี้ CPython จะเห็นว่าคุณมีสองตัวอักษรที่ตรงกันและจะใช้เหมือนกันที่เป็นตัวแทนของ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