ฉันกำลังเขียนคำตอบที่อัปเดตสำหรับ 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_richcomparecase 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;
สรุป
ในการเปรียบเทียบเราเคารพการใช้คลาสย่อยของการเปรียบเทียบก่อน
จากนั้นเราลองเปรียบเทียบกับการนำไปใช้งานของออบเจ็กต์แรกจากนั้นกับสิ่งที่สองถ้ามันไม่ถูกเรียก
ในที่สุดเราก็ใช้การทดสอบเอกลักษณ์เพื่อเปรียบเทียบความเท่าเทียมกัน