12

I noticed I can use the == operator to compare all the native data types (integers, strings, booleans, floating point numbers etc) and also lists, tuples, sets and dictionaries which contain native data types. In these cases the == operator checks if two objects are equal. But in some other cases (trying to compare instances of classes I created) the == operator just checks if the two variables reference the same object (so in these cases the == operator is equivalent to the is operator)

My question is: When does the == operator do more than just comparing identities?

EDIT: I'm using Python 3

snakile
  • 52,936
  • 62
  • 169
  • 241
  • 1
    Essentially, `==` is different from `is`, whenever a class overrides the `__eq__` method it inherits from `object`, so there is no easy answer to this question. Depending on the Python version you are using, things get even more complicated due to the existence of `__cmp__`. Besides curiosity, is there a practical reason for the question? – Dirk Sep 05 '10 at 20:39
  • @Dirk, thanks. I'm asking it mostly out of curiosity, but I also think it's good to know when exactly I can use the == operator to check object equality. – snakile Sep 05 '10 at 20:46

4 Answers4

20

In Python, the == operator is implemented in terms of the magic method __eq__, which by default implements it by identity comparison. You can, however, override the method in order to provide your own concept of object equality. Note, that if you do so, you will usually also override at least __ne__ (which implements the != operator) and __hash__, which computes a hash code for the instance.

I found it very helpful, even in Python, to make my __eq__ implementations comply with the rules set out in the Java language for implementations of the equals method, namely:

  • It is reflexive: for any non-null reference value x, x.equals(x) should return true.
  • It is symmetric: for any non-null reference values x and y, x.equals(y) should return true if and only if y.equals(x) returns true.
  • It is transitive: for any non-null reference values x, y, and z, if x.equals(y) returns true and y.equals(z) returns true, then x.equals(z) should return true.
  • It is consistent: for any non-null reference values x and y, multiple invocations of x.equals(y) consistently return true or consistently return false, provided no information used in equals comparisons on the objects is modified.
  • For any non-null reference value x, x.equals(null) should return false.

the last one should probably replace null with None, but the rules are not as easy here in Python as in Java.

Dirk
  • 30,623
  • 8
  • 82
  • 102
  • Thanks for the rule set for implementing '__eq__' – snakile Sep 05 '10 at 21:18
  • 3
    This "rules" have strong mathematical root: http://en.wikipedia.org/wiki/Equivalence_relation – Tomasz Wysocki Sep 06 '10 at 06:16
  • 1
    tell that to javascript -> http://stackoverflow.com/questions/1995113/strangest-language-feature/1998224#1998224 – wim Jul 04 '11 at 12:45
  • I would add that \__eq\__ in python has a notably different behavior from Java by allowing \__eq\__ to raise NotImplemented, which defers the check for equality to the other class. This blog post is quite helpful: http://jcalderone.livejournal.com/32837.html. This allows you to satisfy the symmetric requirement even when dealing with inheritance. If Java allowed this feature, I believe that the Numbers class could implement Comparable by allowing child classes to determine equality. – Malina Kirn Feb 23 '15 at 22:19
  • Note that you do **not** have to implement the `__ne__` magic method when you implement `__eq__`: by default testing for `a != b` will return `not (a == b)`. – Tom Apr 13 '19 at 09:36
18

== and is are always conceptually distinct: the former delegates to the left-hand object's __eq__ [1], the latter always checks identity, without any delegation. What seems to be confusing you is that object.__eq__ (which gets inherited by default by user-coded classes that don't override it, of course!) is implemented in terms of identity (after all, a bare object has absolutely nothing to check except its identity, so what else could it possibly do?!-).

[1] omitting for simplicity the legacy concept of the __cmp__ method, which is just a marginal complication and changes nothing important in the paragraph's gist;-).

Alex Martelli
  • 854,459
  • 170
  • 1,222
  • 1,395
7

The == does more than comparing identity when ints are involved. It doesn't just check that the two ints are the same object; it actually ensures their values match. Consider:

>>> x=10000
>>> y=10000
>>> x==y,x is y
(True, False)
>>> del x
>>> del y
>>> x=10000
>>> y=x
>>> x==y,x is y
(True, True)

The "standard" Python implementation does some stuff behind the scenes for small ints, so when testing with small values you may get something different. Compare this to the equivalent 10000 case:

>>> del y
>>> del x
>>> x=1
>>> y=1
>>> x==y,x is y
(True, True)
4

What is maybe most important point is that recommendation is to always use:

if myvalue is None:

not

if myvalue == None:

And never to use:

if myvalue is True:

but use:

if myvalue:

This later point is not so supper clear to me as I think there is times to separate the boolean True from other True values like "Alex Martelli" , say there is not False in "Alex Martelli" (absolutely not, it even raises exception :) ) but there is '' in "Alex Martelli" (as is in any other string).

Tony Veijalainen
  • 5,447
  • 23
  • 31