Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Odd behavior of list.remove with arbitrary class - comparing objects

Tags:

python

I've noticed a somehow odd behavior of list.remove that couldn't be explained (by me at least) by going over the C source.

Consider this class:

class A:
    def __init__(self, n1, n2):
        self.n1 = n1
        self.n2 = n2

    def __eq__(self, other):
        print('in eq')
        return vars(self) == vars(other)

And the following code:

a1 = A(1, 2)
a2 = A(1, 2)
li = [a1, a2]
li.remove(a1)

I would expect it to output in eq but it outputs nothing which means A.__eq__ is not being called.

Neither this

a1 = A(1, 2)
a2 = A(2, 3)
li = [a1, a2]
li.remove(a1)
li.remove(a2)

triggers a call to A.__eq__.

Calling remove twice with the same object does end up calling A.__eq__, but (oddly enough) only once:

a1 = A(1, 2)
a2 = A(1, 2)
li = [a1, a2]
li.remove(a1)
li.remove(a1)
# 'in eq'
  1. Why calling remove once doesn't call A.__eq__? How does remove know it found the object to be removed? It seems odd the Python implementation is using memory addresses for comparison even if we override __eq__.

  2. Why does calling remove twice with the same object is calling A.__eq__, and why only once?

like image 977
DeepSpace Avatar asked Feb 04 '26 01:02

DeepSpace


1 Answers

The remove method uses is first before it use ==, i.e. __eq__. So if it finds the object you would like to remove with is, the __eq__ does not get called. It call __eq__ only if it cannot find the object with is. So, when try to remove the object a second time it removes the object that is equal. In this case a2. Now li is empty:

>>> li.remove(a1)
>>> li.remove(a1)
>>> li
[]

When you call li.remove(a1) a third time you will et a ValueError

like image 133
Mike Müller Avatar answered Feb 06 '26 14:02

Mike Müller



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!