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 == b
memanggil a.__eq__(b)
, atau type(a).__eq__(a, b)
.
Secara eksplisit, urutan evaluasi adalah:
- jika
b
tipe adalah subkelas ketat (bukan tipe yang sama) dari a
tipe dan memiliki __eq__
, panggil dan kembalikan nilainya jika perbandingan diimplementasikan,
- lain, jika
a
sudah __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__ called
sebelum 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.c
didefinisikan object_richcompare
untuk 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 == other
kita mengembalikan True
, kalau tidak kita mengembalikan NotImplemented
objeknya. 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_richcompare
fungsi 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.