9

Tôi gặp phải một vấn đề khó hiểu khi đơn vị kiểm tra mô-đun. Mô-đun thực sự đang truyền các giá trị và tôi muốn so sánh các giá trị này.'là' toán tử hoạt động bất ngờ với phao nổi

Có sự khác biệt so với ==is (một phần, tôi hãy cẩn thận về sự khác biệt)

>>> 0.0 is 0.0 
True # as expected 
>>> float(0.0) is 0.0 
True # as expected 

Đúng như dự đoán cho đến bây giờ, nhưng ở đây là "vấn đề" của tôi:

>>> float(0) is 0.0 
False 
>>> float(0) is float(0) 
False 

Tại sao? Ít nhất người cuối cùng thực sự bối rối với tôi. Biểu thị bên trong của float(0)float(0.0) phải bằng nhau. So sánh với == đang hoạt động như mong đợi.

+2

Liên quan: http://stackoverflow.com/questions/132988/is-there-a-difference-between-and-is-in-python – Elazar

+1

Câu hỏi của bạn xứng đáng được trả lời, nhưng nếu bạn gặp phải vấn đề này trong mã thực, mã có lẽ là sai và phải được sửa. Có (hầu như) không có lý do để kiểm tra danh tính tham chiếu giữa các phao theo cách như vậy. – Elazar

+1

Điều kỳ lạ là, mặc dù tôi có thể tạo lại điều này, tất cả 'id (0.0)', 'id (float (0.0))' và 'id (float (0))' trả lại cùng một giá trị. ... Đó là, giá trị là như nhau nếu tôi thực thi cái kia sau cái kia trong hệ vỏ tương tác, nhưng nếu tôi làm 'id (float (0.0)), id (float (0))' (như một bộ tuple) thì các id khác nhau. Có lời giải thích nào không? –

Trả lời

20

Điều này liên quan đến cách hoạt động của is. Nó kiểm tra các tham chiếu thay vì giá trị. Nó trả về True nếu một trong hai đối số được gán cho cùng một đối tượng.

Trong trường hợp này, chúng là các trường hợp khác nhau; float(0)float(0) có cùng giá trị ==, nhưng là các thực thể riêng biệt theo như Python liên quan. Việc triển khai CPython cũng lưu trữ các số nguyên như các đối tượng đơn lẻ trong phạm vi này ->[x | x ∈ ℤ ∧ -5 ≤ x ≤ 256]:

>>> 0.0 is 0.0 
True 
>>> float(0) is float(0) # Not the same reference, unique instances. 
False 

Trong ví dụ này, chúng ta có thể chứng minh các số nguyên bộ nhớ đệm nguyên tắc:

>>> a = 256 
>>> b = 256 
>>> a is b 
True 
>>> a = 257 
>>> b = 257 
>>> a is b 
False 

Bây giờ, nếu nổi được truyền cho float(), các float literal đơn giản được trả về (ngắn mạch được lưu hành), như trong cùng một tham chiếu được sử dụng, vì không cần phải tạo một phao mới từ phao hiện có:

>>> 0.0 is 0.0 
True 
>>> float(0.0) is float(0.0) 
True 

này có thể được chứng minh hơn nữa bằng cách sử dụng int() thêm:

>>> int(256.0) is int(256.0) # Same reference, cached. 
True 
>>> int(257.0) is int(257.0) # Different references are returned, not cached. 
False 
>>> 257 is 257 # Same reference. 
True 
>>> 257.0 is 257.0 # Same reference. As @Martijn Pieters pointed out. 
True 

Tuy nhiên, kết quả của is cũng phụ thuộc vào phạm vi nó đang được thực hiện trong (vượt nhịp của câu hỏi này/giải thích) , vui lòng tham khảo người dùng: @Jim Giải thích tuyệt vời của về code objects. Thậm chí doc trăn bao gồm một phần về hành vi này:

[7] Do tự động thu gom rác, danh sách miễn phí, và bản chất năng động của mô tả, bạn có thể chú ý hành vi dường như không bình thường trong các cách sử dụng nhất định của toán tử is, giống như các toán tử liên quan đến so sánh giữa các phương thức mẫu hoặc các hằng số. Kiểm tra tài liệu của họ để biết thêm thông tin.

+0

'float (17.0) là float (17.0)' trả về 'True' nhưng' float (17.0) là float ('17 .0 ') 'trả về' False' -> là 'float (some_value)' chỉ trả lại float ban đầu để không dụ mới được tạo (trong trường hợp some_value là một phao ...)? –

+0

@Jim đã trả lời số –

+0

CPython này không lưu bộ nhớ cache. Bạn đang nhìn thấy một sự kết hợp của việc gấp liên tục và phương thức khởi tạo 'float' truyền trôi qua trực tiếp cho các trường hợp' là' cho 'True'. – user2357112

9

Nếu một đối tượng float được cung cấp cho float(), CPython * chỉ trả về nó mà không cần tạo một đối tượng mới.

Điều này có thể được nhìn thấy trong PyNumber_Float (cuối cùng được gọi là từ float_new) trong đó đối tượng o được chuyển vào được chọn với PyFloat_CheckExact; nếu True, nó chỉ làm tăng số tham chiếu của nó và trả về nó:

if (PyFloat_CheckExact(o)) { 
    Py_INCREF(o); 
    return o; 
} 

Kết quả là, các id của đối tượng vẫn như cũ. Vì vậy, sự biểu hiện

>>> float(0.0) is float(0.0) 

suy biến thành

>>> 0.0 is 0.0 

Nhưng tại sao mà bằng True? Vâng, CPython có một số nhỏ tối ưu hóa.

Trong trường hợp này, nó sử dụng cùng một đối tượng cho hai lần xuất hiện 0.0 trong lệnh của bạn vì chúng là một phần của the same code object (tuyên bố từ chối trách nhiệm ngắn: chúng nằm trên cùng một đường logic); để kiểm tra is sẽ thành công.

này có thể được chứng thực hơn nữa nếu bạn thực hiện float(0.0) trong dòng riêng biệt (hoặc, giới hạn bởi ;) và sau đó séc sắc:

a = float(0.0); b = float(0.0) # Python compiles these separately 
a is b # False 

Mặt khác, nếu một int (hoặc một str) được cung cấp, CPython sẽ tạo một đối tượng mới float từ đó và trả lại điều đó. Đối với điều này, nó sử dụng tương ứng PyFloat_FromDoublePyFloat_FromString.

Hiệu quả là các đối tượng quay trở lại khác nhau về id s (mà sử dụng để kiểm tra nhân dạng với is):

# Python uses the same object representing 0 to the calls to float 
# but float returns new float objects when supplied with ints 
# Thereby, the result will be False 
float(0) is float(0) 

* Lưu ý: Tất cả các hành vi nêu trước đó áp dụng cho thi hành python trong C tức là CPython. Các triển khai khác có thể thể hiện hành vi khác nhau. Tóm lại, không phụ thuộc vào nó.

+0

Thx cho câu trả lời của bạn với các chi tiết của bạn, tôi sẽ chấp nhận câu trả lời của mrdomoboto là chính xác (tôi ghét quyết định khó khăn này ...) –

+1

Tôi đã thêm phần của bạn vào các đối tượng mã như một chú thích (giải thích tuyệt vời btw!). – ospahiu

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