Bu soruya Python 3 için güncellenmiş bir cevap yazıyorum.
__eq__Python'da nasıl ve hangi sırayla ele alınır?
a == b
Bu genel olarak anlaşılan, ama her zaman durumda, bu a == bbaşlatır a.__eq__(b), veya type(a).__eq__(a, b).
Açıkça, değerlendirme sırası şöyledir:
- Eğer
bçeşidini katı bir alt sınıfı (aynı türü) as türü "ve bir sahiptir __eq__, çağrı ve karşılaştırma uygulandığı takdirde değer döndürür
- Başka, eğer
avar __eq__, diyoruz ve karşılaştırma uygulanırsa iade,
- Aksi takdirde, b'leri aramadık
__eq__ve onda olup olmadığına bakın, sonra arayın ve karşılaştırma uygulanırsa geri dönün
- aksi takdirde, son olarak, aynı karşılaştırmayı kimlik için yapın
is.
Yöntem geri dönerse bir karşılaştırmanın uygulanmadığını biliyoruz NotImplemented.
(Python 2'de __cmp__aranan bir yöntem vardı, ancak kullanımdan kaldırıldı ve Python 3'te kaldırıldı.)
Kabul edilen cevabın bu konuda yanlış olduğunu gösteren B alt sınıfı A'ya izin vererek ilk kontrolün davranışını kendimiz için test edelim:
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
sadece B __eq__ calleddönmeden önce yazdırır False.
Bu tam algoritmayı nasıl biliyoruz?
Buradaki diğer cevaplar eksik ve güncel değil, bu yüzden bilgileri güncelleyeceğim ve bunu nasıl kendiniz bulabileceğinizi göstereceğim.
Bu, C düzeyinde ele alınır.
Burada iki farklı kod bitine bakmamız gerekiyor - __eq__sınıf nesneleri için varsayılan ve varsayılanı veya özel olanı kullanıp kullanmadığına bakılmaksızın yöntemi objectarayan ve çağıran kod .__eq____eq__
Varsayılan __eq__
Looking __eq__up alakalı C API docs gösterileri bize bu __eq__tarafından işlenir tp_richcomparehangi - "object"tipi tanımında cpython/Objects/typeobject.ctanımlanır object_richcompareiçin 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;
Yani burada self == othergeri dönersek True, yoksa NotImplementednesneyi iade ederiz . Bu, kendi __eq__yöntemini uygulamayan herhangi bir nesnenin alt sınıfı için varsayılan davranıştır .
Nasıl __eq__çağrılır
Ardından çağıran PyObject_RichCompare işlevi olan C API belgelerini buluyoruz do_richcompare.
Daha sonra C tanımı tp_richcompareiçin yaratılan fonksiyonun "object"tarafından çağrıldığını görüyoruz, o halde buna do_richcomparebiraz daha yakından bakalım.
Bu işlevdeki ilk kontrol, karşılaştırılan nesnelerin koşulları içindir:
- Hangi değil aynı tip fakat
- ikincinin türü, birinci türünün bir alt sınıfıdır ve
- ikinci türün bir
__eq__yöntemi var,
daha sonra değiştirilen argümanlar ile diğerinin yöntemini çağırın, eğer uygulanırsa değeri döndürür. Bu yöntem uygulanmazsa devam ederiz ...
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);
Daha sonra, __eq__yöntemi ilk türden arayabilir miyiz ve çağırabiliriz. Sonuç NotImplemented olmadığı, yani uygulandığı sürece, onu döndürürüz.
if ((f = Py_TYPE(v)->tp_richcompare) != NULL) {
res = (*f)(v, w, op);
if (res != Py_NotImplemented)
return res;
Py_DECREF(res);
Aksi takdirde, diğer türün yöntemini denemediysek ve oradaysa, sonra deneriz ve karşılaştırma uygulanırsa, onu iade ederiz.
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);
}
Son olarak, herhangi birinin türü için uygulanmaması durumunda bir geri dönüş elde ederiz.
Geri dönüş, nesnenin kimliğini, yani bellekte aynı yerde aynı nesne olup olmadığını kontrol eder - bu, aşağıdakilerle aynı kontroldür 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;
Sonuç
Bir karşılaştırmada, önce karşılaştırmanın alt sınıf uygulamasına saygı duyuyoruz.
Daha sonra, ilk nesnenin gerçeklemesiyle, ardından çağrılmadıysa ikinciyle karşılaştırmaya çalışırız.
Son olarak, eşitlik için karşılaştırma amacıyla bir kimlik testi kullanıyoruz.