Saya menulis jawaban terbaru untuk Python 3 untuk pertanyaan ini.
Bagaimana __eq__ditangani dengan Python dan bagaimana urutannya?
a == b
Secara umum dipahami, tetapi tidak selalu demikian, yang a == bmemanggil a.__eq__(b), atau type(a).__eq__(a, b).
Secara eksplisit, urutan evaluasi adalah:
- jika
btipe adalah subkelas ketat (bukan tipe yang sama) dari atipe dan memiliki __eq__, panggil dan kembalikan nilainya jika perbandingan diimplementasikan,
- lain, jika
asudah __eq__, panggil dan kembalikan jika perbandingan diterapkan,
- lain, lihat apakah kita tidak memanggil b
__eq__dan ia memilikinya, lalu panggil dan kembalikan jika perbandingan diterapkan,
- lain, akhirnya, lakukan perbandingan identitas, sama seperti perbandingan
is.
Kami tahu jika perbandingan tidak diterapkan jika metode tersebut kembali NotImplemented.
(Di Python 2, ada __cmp__metode yang dicari, tetapi tidak digunakan lagi dan dihapus di Python 3.)
Mari kita uji perilaku pemeriksaan pertama untuk diri kita sendiri dengan membiarkan B subclass A, yang menunjukkan bahwa jawaban yang diterima salah dalam hitungan ini:
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
yang hanya mencetak B __eq__ calledsebelum dikembalikan False.
Bagaimana kita mengetahui algoritma lengkap ini?
Jawaban lain di sini tampaknya tidak lengkap dan ketinggalan zaman, jadi saya akan memperbarui informasi dan menunjukkan kepada Anda bagaimana Anda dapat mencarinya sendiri.
Ini ditangani di tingkat C.
Kita perlu melihat dua bit kode yang berbeda di sini - default __eq__untuk objek kelas object, dan kode yang mencari dan memanggil __eq__metode terlepas dari apakah metode itu menggunakan default __eq__atau kustom.
Default __eq__
Mencari __eq__di dokumen C api yang relevan menunjukkan kepada kita bahwa __eq__ditangani oleh tp_richcompare- yang dalam "object"definisi tipe di cpython/Objects/typeobject.cdidefinisikan object_richcompareuntuk 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;
Jadi di sini, jika self == otherkita mengembalikan True, kalau tidak kita mengembalikan NotImplementedobjeknya. Ini adalah perilaku default untuk setiap subclass objek yang tidak mengimplementasikan __eq__metodenya sendiri .
Bagaimana __eq__dipanggil
Kemudian kami menemukan dokumen C API, fungsi PyObject_RichCompare , yang memanggil do_richcompare.
Kemudian kita melihat bahwa tp_richcomparefungsi yang dibuat untuk "object"definisi C dipanggil oleh do_richcompare, jadi mari kita lihat itu lebih dekat.
Pemeriksaan pertama dalam fungsi ini adalah untuk kondisi objek yang dibandingkan:
- yang tidak jenis yang sama, tetapi
- tipe kedua adalah subclass dari tipe pertama, dan
- tipe kedua memiliki
__eq__metode,
lalu panggil metode lain dengan argumen bertukar, mengembalikan nilai jika diterapkan. Jika metode itu tidak diterapkan, kami melanjutkan ...
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);
Selanjutnya kita melihat apakah kita dapat mencari __eq__metode dari tipe pertama dan memanggilnya. Selama hasilnya tidak NotImplemented, yaitu diimplementasikan, kami mengembalikannya.
if ((f = Py_TYPE(v)->tp_richcompare) != NULL) {
res = (*f)(v, w, op);
if (res != Py_NotImplemented)
return res;
Py_DECREF(res);
Lain jika kita tidak mencoba metode tipe lain dan metode itu ada, kita kemudian mencobanya, dan jika perbandingan diterapkan, kita mengembalikannya.
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);
}
Akhirnya, kami mendapatkan fallback jika tidak diterapkan untuk salah satu jenisnya.
Pemeriksaan fallback untuk identitas objek, yaitu, apakah itu objek yang sama di tempat yang sama dalam memori - ini adalah pemeriksaan yang sama untuk 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;
Kesimpulan
Sebagai perbandingan, kami menghormati implementasi subclass dari perbandingan terlebih dahulu.
Kemudian kami mencoba membandingkan dengan implementasi objek pertama, kemudian dengan implementasi objek kedua jika tidak dipanggil.
Akhirnya kami menggunakan tes identitas untuk perbandingan kesetaraan.