2012-07-13 39 views
127

thể trùng lặp:
Python “is” operator behaves unexpectedly with integersTại sao (0-6) là -6 = Sai?

Hôm nay tôi đã cố gắng để gỡ lỗi dự án của tôi và sau một vài tiếng đồng hồ phân tích tôi đã nhận điều này:

>>> (0-6) is -6 
False 

nhưng,

>>> (0-5) is -5 
True 

Bạn có thể giải thích cho tôi không, tại sao? Có thể đây là một loại lỗi hoặc hành vi rất lạ.

> Python 2.7.3 (default, Apr 24 2012, 00:00:54) [GCC 4.7.0 20120414 (prerelease)] on linux2 
>>> type(0-6) 
<type 'int'> 
>>> type(-6) 
<type 'int'> 
>>> type((0-6) is -6) 
<type 'bool'> 
>>> 
+6

cũng thats hết sức wacky – Wug

+25

Điều gì khiến bạn sử dụng 'is' ngay từ đầu? Nó không phải là cái gì đó nên thường được sử dụng trong Python, ngoài trường hợp 'is/is None '. –

+3

@ bình luận của Russel chạm vào móng trên đầu - vấn đề là ai đó dường như đang sử dụng "là" để so sánh các con số và mong đợi nó hoạt động như '=', một kỳ vọng không chính xác. – LarsH

Trả lời

150

Tất cả các số nguyên từ -5 đến 256 bao gồm được lưu trữ như các đối tượng toàn cầu chia sẻ cùng một địa chỉ với CPython, do đó kiểm tra is đi.

Tạo phẩm này được giải thích chi tiết trong http://www.laurentluce.com/posts/python-integer-objects-implementation/ và chúng tôi có thể kiểm tra mã nguồn hiện tại trong http://hg.python.org/cpython/file/tip/Objects/longobject.c.

Cấu trúc cụ thể được sử dụng để chỉ các số nguyên nhỏ và chia sẻ chúng để truy cập nhanh. Nó là một mảng của 262 con trỏ đến các đối tượng số nguyên. Các đối tượng số nguyên được phân bổ trong quá trình khởi tạo trong một khối các đối tượng số nguyên mà chúng ta đã thấy ở trên. Phạm vi các số nguyên nhỏ là từ -5 đến 257. Nhiều chương trình Python dành nhiều thời gian sử dụng các số nguyên trong phạm vi đó nên đây là một quyết định thông minh.

Đây chỉ là chi tiết triển khai của CPython và bạn không nên dựa vào điều này. Ví dụ: Ví dụ, PyPy đã triển khai id của số nguyên để trả về chính nó, vì vậy (0-6) is -6 luôn đúng ngay cả khi chúng là "các đối tượng khác nhau" trong nội bộ; nó cũng cho phép bạn cấu hình cho phép để kích hoạt bộ đệm ẩn số nguyên này, và thậm chí thiết lập giới hạn dưới và trên. Nhưng nói chung, các đối tượng được lấy từ các nguồn gốc khác nhau sẽ không giống nhau. Nếu bạn muốn so sánh bình đẳng, chỉ cần sử dụng ==.

+2

Đó là những gì tôi định nói, nhưng tôi không thể nói đúng. +1 – mpen

+1

Thú vị nghiêng về phía tích cực. Bài báo nói rằng 'nhiều chương trình Python dành nhiều thời gian sử dụng các số nguyên trong phạm vi đó', do đó, các nhà phát triển có lẽ đã đo bằng cách nào đó. Tôi đoán chữ số âm chỉ được sử dụng cho các mã lỗi trong những ngày này ... –

+0

Cảm ơn bạn @KennyTM. –

26

Nó không phải là lỗi. is không phải là một thử nghiệm bình đẳng. == sẽ cho kết quả mong đợi.

Lý do kỹ thuật cho hành vi này là việc triển khai Python miễn phí để xử lý các phiên bản khác nhau của cùng một giá trị như cùng một đối tượng hoặc dưới dạng đối tượng khác nhau. Việc triển khai Python bạn đang sử dụng chọn để làm cho các hằng số nhỏ nhất định chia sẻ cùng một đối tượng cho các lý do tiết kiệm bộ nhớ. Bạn không thể dựa vào hành vi này là phiên bản tương tự với phiên bản hoặc trên các triển khai Python khác nhau.

+2

> 'is' không phải là một thử nghiệm bình đẳng. Điều này. 'is' là một kiểm tra nhận dạng, để xem hai đối tượng có giống hệt nhau hay không. Nó chỉ như vậy sẽ xảy ra rằng trong việc thực hiện CPython, một số đối tượng int được lưu trữ. – Darthfett

+1

+1 Đối với một nhà phát triển không Python, điều này giải thích nó tốt nhất cho tôi. –

29

Số nguyên của cửa hàng Python trong phạm vi -5 - 256 trong trình thông dịch: nó có một nhóm đối tượng số nguyên mà từ đó các số nguyên này được trả về. Đó là lý do tại sao các đối tượng đó giống nhau: (0-5)-5 nhưng không phải là (0-6)-6 vì chúng được tạo ngay tại chỗ.

Đây là nguồn trong mã nguồn của CPython:

#define NSMALLPOSINTS   257 
#define NSMALLNEGINTS   5 
static PyIntObject *small_ints[NSMALLNEGINTS + NSMALLPOSINTS]; 

(view CPython source code: /trunk/Objects/intobject.c).Các mã nguồn bao gồm những nhận xét sau đây:

/* References to small integers are saved in this array so that they 
    can be shared. 
    The integers that are saved are those in the range 
    -NSMALLNEGINTS (inclusive) to NSMALLPOSINTS (not inclusive). 
*/ 

Nhà điều hành is sau đó sẽ so sánh chúng (-5) như bình đẳng, vì họ là cùng một đối tượng (cùng một vị trí bộ nhớ) nhưng hai số nguyên mới khác (-6) sẽ có mặt tại các vị trí bộ nhớ khác nhau (và sau đó is sẽ không trả lại True). Lưu ý rằng 257 trong mã nguồn ở trên là cho các số nguyên dương sao cho là 0 - 256 (bao gồm).

(source)

16

Nó đang xảy ra vì CPython lưu trữ một số nguyên nhỏ và dây nhỏ và mang đến cho tất cả các thể hiện của đối tượng cùng một id().

(0-5)-5 có cùng một giá trị cho id(), đó là không đúng đối với 0-6-6

>>> id((0-6)) 
12064324 
>>> id((-6)) 
12064276 
>>> id((0-5)) 
10022392 
>>> id((-5)) 
10022392 

Tương tự như vậy cho các chuỗi:

>>> x = 'abc' 
>>> y = 'abc' 
>>> x is y 
True 
>>> x = 'a little big string' 
>>> y = 'a little big string' 
>>> x is y 
False 

Để biết thêm chi tiết về chuỗi bộ nhớ đệm, đọc: is operator behaves differently when comparing strings with spaces

+2

vậy tại sao '-6' được coi là" lớn "và' -5' không? Tiêu chuẩn đủ điều kiện cho một thứ gì đó được cosidered "lớn" là gì? – inspectorG4dget

+1

Đối với CPython, -5 đến 256 là "interned" (được lưu trữ). Đó là một lựa chọn thực hiện tùy ý. Nếu một đối tượng interned cho trước được sử dụng rất nhiều, có một khoản tiết kiệm bộ nhớ lớn, nhưng có một chi phí để thực hiện nó (hoặc trong thời gian chạy hoặc bộ nhớ), do đó bạn không muốn làm điều đó cho mọi thứ. –

+0

+1 để hiển thị ID; Tôi vừa thêm nó vào câu trả lời của mình. –

Các vấn đề liên quan