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'
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__.
Why does calling remove twice with the same object is calling A.__eq__, and why only once?
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With