Câu trả lời ngắn gọn là: Không, họ không. Như các tài liệu mà bạn liên kết đến nói:
Các nhà khai thác is
và is not
thử nghiệm cho nhận dạng đối tượng: x is y
là đúng nếu và chỉ nếu x
và y
là cùng một đối tượng.
Là "cùng một đối tượng" không phải là thứ bạn được phép ghi đè. Nếu đối tượng của bạn không phải là đối tượng giống như đối tượng khác, nó không thể giả vờ là.
Vậy… Tại sao? Điều gì sẽ gây hại cho việc bạn ghi đè is
và/hoặc id
? Rõ ràng nó sẽ luôn luôn là một điều ngu ngốc để làm, nhưng Python cho phép bạn làm rất nhiều điều ngu ngốc nếu bạn cố gắng hết sức.
Câu hỏi thường gặp về thiết kế và các tài liệu tương tự không nói. Nhưng tôi nghi ngờ nó chủ yếu là vì nó làm cho nó dễ dàng hơn để gỡ lỗi Python và một số mô-đun thư viện chuẩn sâu hơn, biết có cách nào đó, từ bên trong trình thông dịch, để xác minh rằng hai tên thực sự tham chiếu đến cùng một đối tượng, hoặc in ra id
để đảm bảo tên không thay đổi theo thời gian, v.v. Hãy tưởng tượng gỡ lỗi weakref
hoặc thậm chí pickle
, nếu không có điều đó.
Vì vậy, "chính xác" nghĩa là gì? Vâng, đó là tùy thuộc vào thông dịch viên. Rõ ràng là không thể phân biệt hai trường hợp của cùng một đối tượng ở cấp độ ngôn ngữ, và có thể ở cấp độ thông dịch viên (đặc biệt vì có một API được xác định rõ ràng để cắm vào hầu hết các cài đặt trình thông dịch).
Tất cả các triển khai chính đều xử lý việc này bằng cách trì hoãn khái niệm nhận dạng ở cấp thấp hơn. CPython so sánh giá trị của các PyObject*
con trỏ, Jython nhận dạng so sánh tài liệu tham khảo Java, PyPy làm một is
trên các đối tượng objectspace ...
Đó là giá trị nhìn vào PyPy source, mà đòi hỏi sự "x is y
iff x
và y
là cùng một đối tượng "đúng theo cả hai hướng. Biểu thức cấp cao nhất x is y
là đúng iff, bất kỳ đối tượng wx
và wy
trong không gian đối tượng thích hợp là, wy.is_(wx)
là đúng và is_
được triển khai dưới dạng wy is wx
. Vì vậy, x is y
ở cấp N iff y is x
ở cấp N-1.
Chú ý rằng điều này có nghĩa bạn có thể khá dễ dàng sử dụng PyPy để xây dựng một phương ngữ của Python nơi is
thể được ghi đè, chỉ bằng cách gắn is_
đến một phương pháp dunder __is__
ở cấp cao hơn. Nhưng có một cách đơn giản để làm điều tương tự:
def is_(x, y):
if hasattr(x, '__is__'):
return x.__is__(y)
elif hasattr(y, '__is__'):
return y.__is__(x)
else:
return x is y
Bây giờ chơi với is_(x, y)
thay vì x is y
, và xem nếu bạn có thể tìm thấy bất kỳ rắc rối vui vẻ trước khi thực hiện công việc khó khăn của việc sửa đổi thông dịch viên (thậm chí nếu nó isn' trong trường hợp này).
Vì vậy, is
phải làm gì với id
? Có thể is
được triển khai ở trên cùng của id
— ví dụ: x is y
chỉ cần kiểm tra id(x) == id(y)
? Vâng, id
:
Trả lại “danh tính” của một đối tượng. Đây là một số nguyên được đảm bảo là duy nhất và không đổi cho đối tượng này trong suốt thời gian tồn tại của nó. Hai đối tượng có tuổi thọ không chồng chéo có thể có cùng giá trị id()
.
Vì vậy, id
của một đối tượng là duy nhất và liên tục trong suốt cuộc đời của mình, và x is y
là đúng khi và chỉ khi họ đang cùng một đối tượng, do đó x is y
là đúng khi và chỉ khi id(x) == id(y)
, phải không?
Vâng, id
có thể được phục hồi theo bất cứ điều gì bạn muốn và không được phép ảnh hưởng đến is
.Nếu bạn đã phác thảo định nghĩa rất cẩn thận (hãy nhớ rằng nếu bạn loại bỏ tham chiếu builtins
thành id
, bất kỳ triển khai nào được sử dụng ở đó thậm chí không được bảo đảm tồn tại nữa hoặc hoạt động chính xác nếu nó tồn tại ...), bạn có thể xác định is
trên đầu trang của việc triển khai mặc định là id
.
Nhưng nó sẽ là một điều kỳ lạ để làm. Trong CPython, trong đó id(x)
chỉ "trả về địa chỉ của đối tượng trong bộ nhớ", điều tương tự như giá trị của con trỏ tới đối tượng trong bộ nhớ. Nhưng đó chỉ là một tạo phẩm của CPython; không có gì nói rằng việc triển khai khác phải làm cho id
trả về giá trị cơ bản được sử dụng để so sánh danh tính dưới dạng số nguyên. Trong thực tế, nó không rõ ràng như thế nào bạn thậm chí sẽ làm điều đó trong một thực hiện bằng văn bản trong một ngôn ngữ mà không có con trỏ (có thể được đúc sang số nguyên). Trong PyPy, các id
của một đối tượng thậm chí có thể là một giá trị tính lần đầu tiên nó được truy cập và stashed trong một từ điển trong không gian đối tượng, được khóa bởi chính đối tượng đó.
Đối với __hash__
, bạn đang hiểu sai phần quan trọng của tài liệu.
[...] x.__hash__()
trả về id(x)
.
Phần bạn đã bỏ qua làm cho nó rõ ràng rằng điều này chỉ đúng đối với các trường hợp của lớp do người dùng xác định (không xác định lại __hash__
). Điều này rõ ràng là không đúng đối với, ví dụ: tuple
. Trong ngắn hạn, danh tính không có gì để làm với băm, ngoại trừ cho một số đối tượng nhận dạng làm cho một giá trị băm thuận tiện.
Ví dụ của bạn thực sự khá thú vị. Được yêu thích cho điều đó :) – nneonneo
Định nghĩa của 'băm' mặc định không đúng. Trong CPython, 'PyBaseObject_Type.tp_hash' là' dài _Py_HashPointer (void * p) {long x; size_t y = (size_t) p; y = (y >> 4) | (y << (8 * SIZEOF_VOID_P - 4)); x = (dài) y; if (x == -1) x = -2; return x;} 'Điều này quay địa chỉ bằng 4 bit để giảm va chạm băm, với các bit 2-4 dưới cùng của địa chỉ có lẽ là 0. – eryksun