Tôi đến trễ nhưng, bạn muốn có một số nguồn với câu trả lời của bạn? Tôi sẽ thử và diễn đạt điều này theo cách giới thiệu để nhiều người có thể làm theo.
Một điều tốt về CPython là bạn thực sự có thể thấy nguồn cho việc này. Tôi sẽ sử dụng các liên kết cho bản phát hành 3.5 , nhưng việc tìm các liên kết 2.x tương ứng là không đáng kể.
Trong CPython, hàm C-API xử lý việc tạo một int
đối tượng mới là PyLong_FromLong(long v)
. Mô tả cho chức năng này là:
Việc triển khai hiện tại giữ một mảng các đối tượng số nguyên cho tất cả các số nguyên trong khoảng từ -5 đến 256, khi bạn tạo một int trong phạm vi đó, bạn thực sự chỉ cần lấy lại một tham chiếu đến đối tượng hiện có . Vì vậy, có thể thay đổi giá trị của 1. Tôi nghi ngờ hành vi của Python trong trường hợp này là không xác định. :-)
(Chữ nghiêng của tôi)
Không biết về bạn nhưng tôi thấy điều này và nghĩ: Hãy tìm mảng đó!
Nếu bạn không quan tâm đến mã C đang triển khai CPython, bạn nên ; mọi thứ đều có tổ chức và dễ đọc Đối với trường hợp của chúng ta, chúng ta cần xem trong Objects
thư mục con của cây thư mục mã nguồn chính .
PyLong_FromLong
giao dịch với long
các đối tượng vì vậy không khó để suy luận rằng chúng ta cần nhìn trộm bên trong longobject.c
. Sau khi nhìn vào bên trong, bạn có thể nghĩ mọi thứ thật hỗn loạn; họ, nhưng đừng sợ, chức năng mà chúng tôi đang tìm kiếm đang làm lạnh ở dòng 230 đang chờ chúng tôi kiểm tra. Đây là một chức năng nhỏ để cơ thể chính (không bao gồm khai báo) dễ dàng được dán ở đây:
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;
}
Bây giờ, chúng tôi không có C -code-haxxorz nhưng chúng tôi cũng không ngu ngốc, chúng tôi có thể thấy rằng CHECK_SMALL_INT(ival);
nhìn trộm tất cả chúng tôi một cách quyến rũ; chúng ta có thể hiểu nó có liên quan đến điều này. Hãy cùng kiểm tra nào:
#define CHECK_SMALL_INT(ival) \
do if (-NSMALLNEGINTS <= ival && ival < NSMALLPOSINTS) { \
return get_small_int((sdigit)ival); \
} while(0)
Vì vậy, đó là một macro gọi hàm get_small_int
nếu giá trị ival
thỏa mãn điều kiện:
if (-NSMALLNEGINTS <= ival && ival < NSMALLPOSINTS)
Vậy là gì NSMALLNEGINTS
và NSMALLPOSINTS
? Macro! Họ đây rồi :
#ifndef NSMALLPOSINTS
#define NSMALLPOSINTS 257
#endif
#ifndef NSMALLNEGINTS
#define NSMALLNEGINTS 5
#endif
Vì vậy, điều kiện của chúng tôi là if (-5 <= ival && ival < 257)
cuộc gọi get_small_int
.
Tiếp theo, hãy nhìn vào get_small_int
tất cả vinh quang của nó (tốt, chúng ta sẽ chỉ nhìn vào cơ thể của nó bởi vì đó là nơi có những điều thú vị):
PyObject *v;
assert(-NSMALLNEGINTS <= ival && ival < NSMALLPOSINTS);
v = (PyObject *)&small_ints[ival + NSMALLNEGINTS];
Py_INCREF(v);
Được rồi, khai báo a PyObject
, khẳng định rằng điều kiện trước giữ và thực thi phép gán:
v = (PyObject *)&small_ints[ival + NSMALLNEGINTS];
small_ints
trông rất giống mảng đó chúng tôi đã tìm kiếm, và nó là vậy! Chúng tôi chỉ có thể đọc tài liệu chết tiệt và chúng tôi sẽ biết tất cả cùng! :
/* 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];
Vì vậy, yup, đây là chàng trai của chúng tôi. Khi bạn muốn tạo một cái mới int
trong phạm vi, [NSMALLNEGINTS, NSMALLPOSINTS)
bạn sẽ chỉ cần lấy lại một tham chiếu đến một đối tượng đã tồn tại đã được phổ biến.
Vì tham chiếu đề cập đến cùng một đối tượng, việc phát hành id()
trực tiếp hoặc kiểm tra danh tính với is
nó sẽ trả lại chính xác cùng một điều.
Nhưng, khi nào họ được phân bổ ??
Trong quá trình khởi tạo,_PyLong_Init
Python sẽ sẵn sàng nhập vào một vòng lặp for làm điều này cho bạn:
for (ival = -NSMALLNEGINTS; ival < NSMALLPOSINTS; ival++, v++) {
Kiểm tra nguồn để đọc cơ thể vòng lặp!
Tôi hy vọng lời giải thích của tôi đã làm cho bạn C mọi thứ rõ ràng (chơi chữ rõ ràng có ý định).
Nhưng , 257 is 257
? Có chuyện gì vậy?
Điều này thực sự dễ giải thích hơn, và tôi đã cố gắng làm điều đó rồi ; đó là do thực tế là Python sẽ thực thi câu lệnh tương tác này dưới dạng một khối duy nhất:
>>> 257 is 257
Trong quá trình bổ sung tuyên bố này, CPython sẽ thấy rằng bạn có hai nghĩa đen phù hợp và sẽ sử dụng cùng một PyLongObject
đại diện 257
. Bạn có thể thấy điều này nếu bạn tự biên soạn và kiểm tra nội dung của nó:
>>> codeObj = compile("257 is 257", "blah!", "exec")
>>> codeObj.co_consts
(257, None)
Khi CPython thực hiện thao tác, giờ nó sẽ tải chính xác cùng một đối tượng:
>>> import dis
>>> dis.dis(codeObj)
1 0 LOAD_CONST 0 (257) # dis
3 LOAD_CONST 0 (257) # dis again
6 COMPARE_OP 8 (is)
Vậy is
sẽ trở về True
.