Are objects with the same id always equal when comparing them with ==?

PythonEquality

Python Problem Overview


If I have two objects o1 and o2, and we know that

id(o1) == id(o2)

returns true.

Then, does it follow that

o1 == o2

Or is this not always the case? The paper I'm working on says this is not the case, but in my opinion it should be true!

Python Solutions


Solution 1 - Python

Not always:

>>> nan = float('nan')
>>> nan is nan
True

or formulated the same way as in the question:

>>> id(nan) == id(nan)
True

but

>>> nan == nan
False

NaN is a strange thing. Per definition it is not equal nor less or greater than itself. But it is the same object. More details why all comparisons have to return False in this SO question.

Solution 2 - Python

The paper is right. Consider the following.

class WeirdEquals:
    def __eq__(self, other):
        return False

w = WeirdEquals()
print("id(w) == id(w)", id(w) == id(w))
print("w == w", w == w)

Output is this:

id(w) == id(w) True
w == w False

Solution 3 - Python

id(o1) == id(o2) does not imply o1 == o2.

Let's have a look at this Troll which overrides __eq__ to always return False.

>>> class Troll(object):
...     def __eq__(self, other):
...         return False
... 
>>> a = Troll()
>>> b = a
>>> id(a) == id(b)
True
>>> a == b
False

That being said, there should be very few examples in the standard library where the object-ids match but __eq__ can return False anyway, kudos @MarkMüller for finding a good example.

So either the objects are insane, very special (like nan), or concurrency bites you. Consider this extreme example, where Foo has a more reasonable __eq__ method (which 'forgets' to check the ids) and f is f is always True.

import threading

class Foo(object):
    def __init__(self):
        self.x = 1

    def __eq__(self, other):
        return isinstance(other, Foo) and self.x == other.x

f = Foo()

class MutateThread(threading.Thread):
    def run(self):
        while True:
            f.x = 2
            f.x = 1

class CheckThread(threading.Thread):
    def run(self):
        i = 1
        while True:
            if not (f == f):
                print 'loop {0}: f != f'.format(i) 
            i += 1

MutateThread().start()
CheckThread().start()

Output:

$ python eqtest.py
loop 520617: f != f
loop 1556675: f != f
loop 1714709: f != f
loop 2436222: f != f
loop 3210760: f != f
loop 3772996: f != f
loop 5610559: f != f
loop 6065230: f != f
loop 6287500: f != f
...

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionJonas KaufmannView Question on Stackoverflow
Solution 1 - PythonMike MüllerView Answer on Stackoverflow
Solution 2 - PythonrecursiveView Answer on Stackoverflow
Solution 3 - PythontimgebView Answer on Stackoverflow