Почему мой объект правильно удален из списка, когда __eq__ не вызывается?

У меня есть следующий код, который заставляет меня почесывать голову -

class Element:
    def __init__(self, name):
        self.name = name

    def __repr__(self):
        return self.name

def eq(self, other):
    print('comparing {} to {} ({})'.format(self.name, 
        other.name, 
        self.name == other.name))

    return self.name == other.name

Element.__eq__ = eq
elements = [
    Element('a'), 
    Element('b'), 
    Element('c'), 
    Element('d')    
]

print('before {}'.format(elements))
elements.remove(elements[3])
print('after {}'.format(elements))

Что дает следующий вывод -

before [a, b, c, d]
comparing a to d (False)
comparing b to d (False)
comparing c to d (False)
after [a, b, c]

Почему не eq() вывод comparing d to d (True)?

Причина, по которой я заменяю обезьяны __eq__ вместо простого ее применения в классе Element, заключается в том, что я тестирую, как работает патч обезьяны, прежде чем я реализую его с помощью одной из библиотек, которые я использую.

Ответы

Ответ 1

Четвертый элемент - это точно такой же объект с объектом, который проходит код (elements[3]).

Другими словами,

>>> elements[3] is elements[3]
True
>>> elements[3] == elements[3]
True

Таким образом, нет необходимости проверять равенство, потому что они (?) идентичны (одни и те же).

Проверка равенства выполняется, если они не идентичны. Например, будет вызываться __eq__, если код передает другой объект с тем же значением:

elements.remove(Element('d'))

Ответ 2

Метод Python list.remove() сначала проверяет, идентичны ли оба объекта, в противном случае возвращается к обычным методам сравнения, например __eq__. Таким образом, в этом случае, когда оба объекта идентичны, они удаляются из списка.

listremove(PyListObject *self, PyObject *v)
{
    Py_ssize_t i;

    for (i = 0; i < Py_SIZE(self); i++) {
        int cmp = PyObject_RichCompareBool(self->ob_item[i], v, Py_EQ);
        ...

Здесь PyObject_RichCompareBool(PyObject *o1, PyObject *o2, int opid) используется для сравнения и из его документов:

Если o1 и o2 - один и тот же объект, PyObject_RichCompareBool() будет всегда возвращайте 1 для Py_EQ и 0 для Py_NE.