简体   繁体   中英

Why is my object properly removed from a list when __eq__ isn't being called?

I have the following code, which is making me scratch my head -

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))

Which yields the following output -

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

Why isn't eq() outputting comparing d to d (True) ?

The reason I'm monkey patching __eq__ instead of simply implementing it in my Element class is because I'm testing how monkey patching works before I implement it with one of the libraries I'm using.

The fourth element is the exactly same object with the object the code is passing ( elements[3] ).

In other word,

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

So, no need to check the equality because they(?) are identical (same) one.

Equality check will happen if they are not identical. For example, __eq__ will be called if the code passes another object with the same value:

elements.remove(Element('d'))

Python's list.remove() method first checks whether the both objects are identical otherwise falls back to regular comparison methods like __eq__ in this case. So, in this case as both objects are identical it is removed from the list.

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);
        ...

Here PyObject_RichCompareBool(PyObject *o1, PyObject *o2, int opid) is being used for comparison, and from its docs:

If o1 and o2 are the same object, PyObject_RichCompareBool() will always return 1 for Py_EQ and 0 for Py_NE .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM