Sto scrivendo una risposta aggiornata per Python 3 a questa domanda.
Come viene __eq__gestito in Python e in quale ordine?
a == b
È generalmente compreso, ma non sempre è così, che a == binvoca a.__eq__(b), o type(a).__eq__(a, b).
Esplicitamente, l'ordine di valutazione è:
- se
bil tipo di è una sottoclasse rigorosa (non lo stesso tipo) del atipo di se ha un __eq__, chiamalo e restituisci il valore se il confronto è implementato,
- altrimenti, se
aha __eq__, chiamalo e restituiscilo se il confronto è implementato,
- altrimenti, vedi se non abbiamo chiamato b
__eq__e ce l' ha, quindi chiamalo e restituiscilo se il confronto è implementato,
- altrimenti, infine, fai il confronto per l'identità, lo stesso confronto di
is.
Sappiamo se un confronto non viene implementato se il metodo restituisce NotImplemented.
(In Python 2, c'era un __cmp__metodo che è stato cercato, ma è stato deprecato e rimosso in Python 3.)
Testiamo il comportamento del primo controllo per noi stessi lasciando che B sottoclasse A, il che mostra che la risposta accettata è sbagliata su questo conteggio:
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
che stampa solo B __eq__ calledprima della restituzione False.
Come conosciamo questo algoritmo completo?
Le altre risposte qui sembrano incomplete e non aggiornate, quindi aggiornerò le informazioni e ti mostrerò come potresti cercarle da solo.
Questo è gestito a livello C.
Abbiamo bisogno di esaminare due diversi bit di codice qui: il valore predefinito __eq__per gli oggetti di classe objecte il codice che cerca e chiama il __eq__metodo indipendentemente dal fatto che utilizzi quello predefinito __eq__o personalizzato.
Predefinito __eq__
Cercare __eq__nei documenti C api pertinenti ci mostra che __eq__è gestito da tp_richcompare- che nella "object"definizione del tipo in cpython/Objects/typeobject.cè definito in object_richcomparefor 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;
Quindi qui, se self == othertorniamo True, altrimenti restituiamo l' NotImplementedoggetto. Questo è il comportamento predefinito per qualsiasi sottoclasse di oggetto che non implementa il proprio __eq__metodo.
Come __eq__viene chiamato
Quindi troviamo i documenti dell'API C, la funzione PyObject_RichCompare , che chiama do_richcompare.
Quindi vediamo che la tp_richcomparefunzione, creata per la "object"definizione C è chiamata da do_richcompare, quindi guardiamola un po 'più da vicino.
Il primo controllo in questa funzione è per le condizioni degli oggetti confrontati:
- non sono dello stesso tipo, ma
- il tipo del secondo è una sottoclasse del tipo del primo, e
- il secondo tipo ha un
__eq__metodo,
quindi chiama il metodo dell'altro con gli argomenti scambiati, restituendo il valore se implementato. Se questo metodo non è implementato, continuiamo ...
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);
Successivamente vediamo se possiamo cercare il __eq__metodo dal primo tipo e chiamarlo. Finché il risultato non è NotImplemented, ovvero è implementato, lo restituiamo.
if ((f = Py_TYPE(v)->tp_richcompare) != NULL) {
res = (*f)(v, w, op);
if (res != Py_NotImplemented)
return res;
Py_DECREF(res);
Altrimenti, se non abbiamo provato il metodo dell'altro tipo ed è lì, lo proviamo e se il confronto è implementato, lo restituiamo.
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);
}
Infine, otteniamo un fallback nel caso in cui non sia implementato per nessuno dei due tipi.
Il fallback controlla l'identità dell'oggetto, cioè se è lo stesso oggetto nello stesso punto della memoria - questo è lo stesso controllo di 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;
Conclusione
In un confronto, rispettiamo prima l'implementazione della sottoclasse del confronto.
Quindi tentiamo il confronto con l'implementazione del primo oggetto, quindi con il secondo se non è stato chiamato.
Infine usiamo un test per l'identità per il confronto per l'uguaglianza.