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 == b
baş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ü) a
s 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
a
var __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__ called
dö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 object
arayan 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_richcompare
hangi - "object"
tipi tanımında cpython/Objects/typeobject.c
tanımlanır object_richcompare
iç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 == other
geri dönersek True
, yoksa NotImplemented
nesneyi 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_richcompare
için yaratılan fonksiyonun "object"
tarafından çağrıldığını görüyoruz, o halde buna do_richcompare
biraz 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.