ฉันกำลังเขียนคำตอบที่อัปเดตสำหรับ Python 3 สำหรับคำถามนี้
มี__eq__
การจัดการอย่างไรใน Python และเรียงลำดับอย่างไร?
a == b
มันเป็นที่เข้าใจกันโดยทั่วไป แต่ไม่เสมอกรณีที่a == b
จะเรียกหรือa.__eq__(b)
type(a).__eq__(a, b)
อย่างชัดเจนลำดับของการประเมินคือ:
- ถ้า
b
ประเภทเป็นคลาสย่อยที่เข้มงวด (ไม่ใช่ประเภทเดียวกัน) ของa
ประเภทและมีให้__eq__
เรียกและส่งคืนค่าหากมีการใช้การเปรียบเทียบ
- อื่นถ้า
a
มี__eq__
ให้เรียกและส่งคืนหากมีการใช้การเปรียบเทียบ
- อื่นดูว่าเราไม่ได้เรียก b
__eq__
และมันมีแล้วโทรและส่งคืนหากมีการใช้การเปรียบเทียบ
- อื่นในที่สุดก็ทำเปรียบเทียบลักษณะ,
is
การเปรียบเทียบเช่นเดียวกับ
NotImplemented
เรารู้ว่าถ้าเปรียบเทียบไม่ได้ดำเนินการหากผลตอบแทนที่วิธีการ
(ใน Python 2 มีไฟล์ __cmp__
เมธอดที่ถูกมองหา แต่ถูกเลิกใช้งานและลบออกใน Python 3)
ลองทดสอบพฤติกรรมของเช็คแรกด้วยตัวเราเองโดยปล่อยให้ B subclass A ซึ่งแสดงว่าคำตอบที่ยอมรับนั้นผิดในการนับนี้:
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
ซึ่งจะพิมพ์B __eq__ called
ก่อนกลับFalse
เท่านั้น
เราจะรู้อัลกอริทึมทั้งหมดนี้ได้อย่างไร?
คำตอบอื่น ๆ ที่นี่ดูเหมือนจะไม่สมบูรณ์และล้าสมัยดังนั้นฉันจะอัปเดตข้อมูลและแสดงให้คุณเห็นว่าคุณจะค้นหาสิ่งนี้ด้วยตัวคุณเองได้อย่างไร
สิ่งนี้ได้รับการจัดการที่ระดับ C
เราต้องดูรหัสสองบิตที่แตกต่างกันที่นี่ - ค่าเริ่มต้น__eq__
สำหรับวัตถุของคลาสobject
และรหัสที่ค้นหาและเรียกใช้__eq__
เมธอดโดยไม่คำนึงว่าจะใช้ค่าเริ่มต้นหรือไม่__eq__
หรือแบบกำหนดเอง
ค่าเริ่มต้น __eq__
มอง__eq__
ขึ้นมาในที่เกี่ยวข้องเอกสาร C APIแสดงให้เราเห็นว่า__eq__
จะถูกจัดการโดยtp_richcompare
- ซึ่งใน"object"
การกำหนดประเภทในการcpython/Objects/typeobject.c
กำหนดไว้ในสำหรับobject_richcompare
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
res = (self == other) ? Py_True : Py_NotImplemented;
Py_INCREF(res);
break;
ที่นี่ถ้าself == other
เรากลับมาTrue
ก็จะส่งคืนNotImplemented
วัตถุ นี่เป็นลักษณะการทำงานเริ่มต้นสำหรับคลาสย่อยของอ็อบเจ็กต์ที่ไม่ได้ใช้__eq__
วิธีการของตัวเอง
วิธีการ__eq__
ได้รับเรียกว่า
จากนั้นเราจะพบเอกสาร C API ที่PyObject_RichComparedo_richcompare
ฟังก์ชั่นที่โทร
จากนั้นเราจะเห็นว่าtp_richcompare
ฟังก์ชั่นที่สร้างขึ้นสำหรับ"object"
นิยาม C ถูกเรียกโดยdo_richcompare
ดังนั้นเรามาดูกันอย่างละเอียดอีกนิด
การตรวจสอบครั้งแรกในฟังก์ชั่นนี้มีไว้สำหรับเงื่อนไขของการเปรียบเทียบวัตถุ:
- มีไม่ชนิดเดียวกัน แต่
- ประเภทที่สองคือคลาสย่อยของประเภทแรกและ
- ประเภทที่สองมี
__eq__
วิธีการ
จากนั้นเรียกใช้เมธอดของอีกฝ่ายโดยมีการแลกเปลี่ยนอาร์กิวเมนต์ส่งคืนค่าหากใช้งาน หากวิธีนี้ไม่ได้ใช้เราดำเนินการต่อ ...
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);
ต่อไปเราจะดูว่าเราสามารถค้นหา__eq__
วิธีการจากประเภทแรกและเรียกมันได้หรือไม่ ตราบเท่าที่ผลลัพธ์ไม่ได้นำไปใช้นั่นคือมีการนำไปใช้งานเราจะส่งคืน
if ((f = Py_TYPE(v)->tp_richcompare) != NULL) {
res = (*f)(v, w, op);
if (res != Py_NotImplemented)
return res;
Py_DECREF(res);
หากเราไม่ได้ลองใช้วิธีการของประเภทอื่นและอยู่ที่นั่นเราจะลองและหากการเปรียบเทียบถูกนำไปใช้เราจะส่งคืน
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);
}
สุดท้ายเราได้รับทางเลือกในกรณีที่ไม่ได้ใช้กับประเภทใดประเภทหนึ่ง
ทางเลือกจะตรวจสอบข้อมูลประจำตัวของวัตถุนั่นคือว่าเป็นวัตถุเดียวกันที่ตำแหน่งเดียวกันในหน่วยความจำหรือไม่ซึ่งเป็นการตรวจสอบเช่นเดียวกับ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;
สรุป
ในการเปรียบเทียบเราเคารพการใช้คลาสย่อยของการเปรียบเทียบก่อน
จากนั้นเราลองเปรียบเทียบกับการนำไปใช้งานของออบเจ็กต์แรกจากนั้นกับสิ่งที่สองถ้ามันไม่ถูกเรียก
ในที่สุดเราก็ใช้การทดสอบเอกลักษณ์เพื่อเปรียบเทียบความเท่าเทียมกัน