Python lưu trữ các số nguyên trong phạm vi [-5, 256]
, vì vậy, các số nguyên trong phạm vi đó cũng giống hệt nhau.
Những gì bạn thấy là trình biên dịch Python tối ưu hóa các chữ giống hệt nhau khi một phần của cùng một văn bản.
Khi gõ vào Python shell mỗi dòng là một tuyên bố hoàn toàn khác nhau, phân tích cú pháp trong một thời điểm khác nhau, do đó:
>>> a = 257
>>> b = 257
>>> a is b
False
Nhưng nếu bạn đặt cùng mã vào một tập tin:
$ echo 'a = 257
> b = 257
> print a is b' > testing.py
$ python testing.py
True
Điều này xảy ra bất cứ khi nào trình phân tích cú pháp có cơ hội phân tích nơi sử dụng các chữ cái, ví dụ: khi xác định hàm trong trình thông dịch tương tác:
>>> def test():
... a = 257
... b = 257
... print a is b
...
>>> dis.dis(test)
2 0 LOAD_CONST 1 (257)
3 STORE_FAST 0 (a)
3 6 LOAD_CONST 1 (257)
9 STORE_FAST 1 (b)
4 12 LOAD_FAST 0 (a)
15 LOAD_FAST 1 (b)
18 COMPARE_OP 8 (is)
21 PRINT_ITEM
22 PRINT_NEWLINE
23 LOAD_CONST 0 (None)
26 RETURN_VALUE
>>> test()
True
>>> test.func_code.co_consts
(None, 257)
Lưu ý cách mã được biên dịch chứa một hằng số cho 257
.
Tóm lại, trình biên dịch bytecode của Python không thể thực hiện tối ưu hóa lớn (như các ngôn ngữ kiểu tĩnh), nhưng nó có nhiều hơn bạn nghĩ. Một trong những điều này là phân tích việc sử dụng các chữ và tránh sao chép chúng.
Lưu ý rằng điều này không phải làm gì với bộ nhớ cache, vì nó hoạt động cũng cho nổi, mà không có một bộ nhớ cache:
>>> a = 5.0
>>> b = 5.0
>>> a is b
False
>>> a = 5.0; b = 5.0
>>> a is b
True
Đối literals phức tạp hơn, như các bộ, nó "không làm việc ":
>>> a = (1,2)
>>> b = (1,2)
>>> a is b
False
>>> a = (1,2); b = (1,2)
>>> a is b
False
Nhưng literals bên trong tuple được chia sẻ:
>>> a = (257, 258)
>>> b = (257, 258)
>>> a[0] is b[0]
False
>>> a[1] is b[1]
False
>>> a = (257, 258); b = (257, 258)
>>> a[0] is b[0]
True
>>> a[1] is b[1]
True
Về lý do tại sao bạn thấy rằng hai số PyInt_Object
được tạo, tôi muốn đoán rằng việc này được thực hiện để tránh so sánh theo nghĩa đen. ví dụ, số 257
có thể được thể hiện bằng nhiều literals:
>>> 257
257
>>> 0x101
257
>>> 0b100000001
257
>>> 0o401
257
Các phân tích cú pháp có hai lựa chọn:
- Chuyển đổi các literals đối với một số cơ sở chung trước khi tạo các số nguyên, và xem nếu literals là tương đương. sau đó tạo một đối tượng số nguyên duy nhất.
- Tạo các đối tượng số nguyên và xem chúng có bằng nhau hay không. Nếu có, chỉ giữ một giá trị duy nhất và gán nó cho tất cả các chữ, nếu không, bạn đã có số nguyên để gán.
Có lẽ trình phân tích cú pháp Python sử dụng phương pháp thứ hai, tránh viết lại mã chuyển đổi và cũng dễ mở rộng hơn (ví dụ: nó hoạt động với các phao).
Đọc file Python/ast.c
, chức năng mà phân tích tất cả các số là parsenumber
, trong đó kêu gọi PyOS_strtoul
để có được những giá trị số nguyên (ví intgers) và cuối cùng gọi PyLong_FromString
:
x = (long) PyOS_strtoul((char *)s, (char **)&end, 0);
if (x < 0 && errno == 0) {
return PyLong_FromString((char *)s,
(char **)0,
0);
}
Như bạn có thể thấy ở đây trình phân tích cú pháp thực hiện không kiểm tra xem nó đã tìm thấy một số nguyên với giá trị đã cho hay chưa và do đó điều này giải thích tại sao bạn thấy rằng hai đối tượng int được tạo, và điều này cũng có nghĩa là dự đoán của tôi là chính xác: t tạo ra các hằng số và chỉ sau đó tối ưu hóa bytecode để sử dụng cùng một đối tượng cho các hằng số bằng nhau.
Mã kiểm tra này phải ở đâu đó trong Python/compile.c
hoặc Python/peephole.c
, vì đây là các tệp chuyển đổi AST thành bytecode.
Cụ thể, chức năng compiler_add_o
có vẻ như là chức năng thực hiện. Có nhận xét này trong compiler_lambda
:
/* Make None the first constant, so the lambda can't have a
docstring. */
if (compiler_add_o(c, c->u->u_consts, Py_None) < 0)
return 0;
Vì vậy, nó có vẻ như compiler_add_o
được sử dụng để chèn các hằng số cho các chức năng/lambdas, vv Chức năng compiler_add_o
lưu trữ các hằng số vào một đối tượng dict
, và từ này ngay lập tức sau đó hằng số bằng sẽ rơi vào cùng một vị trí, dẫn đến một hằng số duy nhất trong bytecode cuối cùng.
Bạn chỉ đang cố gắng hiểu nguồn Python hoạt động như thế nào, hoặc bạn đang cố gắng hiểu những gì upshot dành cho mã được viết bằng Python? Bởi vì upshot cho mã được viết bằng Python là "đây là một chi tiết thực hiện, không bao giờ dựa vào nó xảy ra hoặc không xảy ra". – BrenBarn
Tôi sẽ không dựa vào chi tiết triển khai. Tôi chỉ tò mò và cố gắng đột nhập vào mã nguồn. – felix021
Liên quan: [Python "là" toán tử hoạt động bất ngờ với số nguyên] (http://stackoverflow.com/questions/306313/python-is-operator-behaves-unexpectedly-with-integers) – Blckknght