Tôi đang viết một câu trả lời cập nhật cho Python 3 cho câu hỏi này.
Làm thế nào được __eq__
xử lý trong Python và theo thứ tự nào?
a == b
Nó được hiểu một cách tổng quát, nhưng không phải lúc nào cũng vậy, nó a == b
dẫn đến a.__eq__(b)
, hoặc type(a).__eq__(a, b)
.
Rõ ràng, thứ tự đánh giá là:
- if
b
's type là một lớp con nghiêm ngặt (không cùng kiểu) của a
' s type và có một __eq__
, gọi nó và trả về giá trị nếu so sánh được thực hiện,
- khác, nếu
a
có __eq__
, hãy gọi nó và trả lại nó nếu so sánh được triển khai,
- khác, hãy xem nếu chúng tôi không gọi b
__eq__
và nó có nó, sau đó gọi và trả về nó nếu so sánh được thực hiện,
- khác, cuối cùng, thực hiện so sánh cho danh tính, so sánh tương tự như
is
.
Chúng tôi biết nếu một so sánh không được triển khai nếu phương thức trả về NotImplemented
.
(Trong Python 2, có một __cmp__
phương thức đã được tìm kiếm, nhưng nó không được chấp nhận và bị loại bỏ trong Python 3)
Hãy tự mình kiểm tra hành vi của séc đầu tiên bằng cách cho B phân lớp A, điều này cho thấy rằng câu trả lời được chấp nhận là sai trong số này:
class A:
value = 3
def __eq__(self, other):
print('A __eq__ called')
return self.value == other.value
class B(A):
value = 4
def __eq__(self, other):
print('B __eq__ called')
return self.value == other.value
a, b = A(), B()
a == b
mà chỉ in B __eq__ called
trước khi trả lại False
.
Làm sao chúng ta biết được thuật toán đầy đủ này?
Các câu trả lời khác ở đây có vẻ chưa đầy đủ và lỗi thời, vì vậy tôi sẽ cập nhật thông tin và chỉ cho bạn cách bạn có thể tự tra cứu vấn đề này.
Điều này được xử lý ở mức C.
Chúng ta cần xem xét hai bit mã khác nhau ở đây - mặc định __eq__
cho các đối tượng của lớp object
và mã tra cứu và gọi __eq__
phương thức bất kể nó sử dụng mặc định __eq__
hay tùy chỉnh.
Mặc định __eq__
Tra __eq__
cứu trong tài liệu C api có liên quan cho chúng ta thấy rằng nó __eq__
được xử lý bởi tp_richcompare
- mà trong "object"
định nghĩa kiểu trong cpython/Objects/typeobject.c
được định nghĩa trong object_richcompare
cho case Py_EQ:
.
case Py_EQ:
/* Return NotImplemented instead of False, so if two
objects are compared, both get a chance at the
comparison. See issue #1393. */
res = (self == other) ? Py_True : Py_NotImplemented;
Py_INCREF(res);
break;
Vì vậy, ở đây, nếu self == other
chúng ta trả về True
, nếu chúng ta trả về NotImplemented
đối tượng khác. Đây là hành vi mặc định cho bất kỳ lớp con nào của đối tượng không triển khai __eq__
phương thức riêng của nó .
Làm thế nào __eq__
được gọi là
Sau đó, chúng tôi tìm tài liệu API C, hàm PyObject_RichCompare , gọi do_richcompare
.
Sau đó, chúng ta thấy rằng tp_richcompare
hàm, được tạo cho "object"
định nghĩa C được gọi bởi do_richcompare
, vì vậy hãy xem xét kỹ hơn một chút.
Kiểm tra đầu tiên trong hàm này là các điều kiện mà các đối tượng được so sánh:
- là không cùng loại, nhưng
- loại thứ hai là một lớp con của loại thứ nhất và
- loại thứ hai có một
__eq__
phương thức,
sau đó gọi phương thức kia với các đối số được hoán đổi, trả về giá trị nếu được thực thi. Nếu phương pháp đó không được triển khai, chúng tôi tiếp tục ...
if (!Py_IS_TYPE(v, Py_TYPE(w)) &&
PyType_IsSubtype(Py_TYPE(w), Py_TYPE(v)) &&
(f = Py_TYPE(w)->tp_richcompare) != NULL) {
checked_reverse_op = 1;
res = (*f)(w, v, _Py_SwappedOp[op]);
if (res != Py_NotImplemented)
return res;
Py_DECREF(res);
Tiếp theo, chúng ta xem liệu chúng ta có thể tra cứu __eq__
phương thức từ kiểu đầu tiên hay không và gọi nó. Miễn là kết quả không phải là NotImplemented, nghĩa là nó được thực hiện, chúng tôi trả lại nó.
if ((f = Py_TYPE(v)->tp_richcompare) != NULL) {
res = (*f)(v, w, op);
if (res != Py_NotImplemented)
return res;
Py_DECREF(res);
Nếu không, nếu chúng tôi không thử phương thức của loại khác và nó ở đó, thì chúng tôi sẽ thử nó và nếu so sánh được thực hiện, chúng tôi trả về nó.
if (!checked_reverse_op && (f = Py_TYPE(w)->tp_richcompare) != NULL) {
res = (*f)(w, v, _Py_SwappedOp[op]);
if (res != Py_NotImplemented)
return res;
Py_DECREF(res);
}
Cuối cùng, chúng tôi nhận được một dự phòng trong trường hợp nó không được triển khai cho một trong hai loại của một người.
Dự phòng kiểm tra danh tính của đối tượng, nghĩa là nó có phải là cùng một đối tượng tại cùng một vị trí trong bộ nhớ hay không - đây là cách kiểm tra tương tự như đối với self is other
:
/* If neither object implements it, provide a sensible default
for == and !=, but raise an exception for ordering. */
switch (op) {
case Py_EQ:
res = (v == w) ? Py_True : Py_False;
break;
Phần kết luận
Để so sánh, chúng tôi tôn trọng việc triển khai lớp con của so sánh trước tiên.
Sau đó, chúng tôi thử so sánh với việc triển khai đối tượng đầu tiên, sau đó với đối tượng thứ hai nếu nó không được gọi.
Cuối cùng, chúng tôi sử dụng một bài kiểm tra nhận dạng để so sánh cho sự bình đẳng.