There are numerous factors to keep in mind concerning identity, equality, and comparisons in Python. This discussion assumes you already understand the distinction between identity (also called reference equality) and equality (also known as value equality) as a general Computer Science concept. You can find more information on this distinction quite a while ago.
In Python, identity comparison employs the is and is not operators. These operators verify if two variables reference the same object in memory. Essentially, ob1 is ob2 is functionally identical to id(ob1) == id(ob2).
Conversely, equality comparison utilizes the == and != operators. The behavior of these operators is governed by the __eq__ and __ne__ methods within an object’s class. This enables customization of their behavior (operator overloading) through implementation in your class. If not implemented, Python defaults to using object.__eq__ and object.__ne__ through the attribute resolution algorithm. These default methods perform a basic identity/reference comparison. It’s important to note that classes like str or int implement their own __eq__ and __ne__ methods. Therefore, str.__eq__ is object.__eq__ evaluates to False. For a deeper dive into this, you can consult here.
All types in Python, directly or indirectly, are subtypes of the object type. Consequently, they inherit the default comparison behavior from object. However, types can override this default by implementing rich comparison methods like __lt__(), as explained in the “Basic customization” section (not provided here).
The default behavior for equality comparison (== and !=) hinges on the identity of the objects being compared. This means that objects with the same identity are considered equal, while those with different identities are unequal. This default behavior stems from the principle that all objects should be reflexive, meaning if x is y, then x == y.
While I mentioned implementing the __ne__ method earlier, it’s not strictly required. Defining __eq__ alone is sufficient to customize equality behavior for a class. When != is used, Python attempts to locate a __ne__ method. If not found, it defaults to object.__ne__, which in turn calls __eq__ on the instance (the method you implemented) and negates the result. For a more detailed discussion on this, refer to here.
It’s worth noting that in Python 3.x, the default implementation of __ne__ indeed invokes __eq__ and negates the result, though there are technical nuances involved (further reading recommended).
Unlike JavaScript, Python maintains a clear distinction between types in comparisons. does not have type coercion/implicit conversion. This means equality comparisons (__eq__, __ne__) in classes like int and str (or methods like __add__) won’t automatically convert types if an object of a different type is encountered. For instance, 5 == “5” evaluates to False in Python, while it’s True in JavaScript. Similarly, 3 + “5” throws an Exception in Python, whereas JavaScript converts the integer to a string and returns “35”.
The in operator offers a useful and expressive way to check for membership. Regarding its behavior with value or reference equality, answer provides an excellent explanation. In essence, “in” relies on the __contains__ method of the container object. For common containers like list, tuple, set, frozenset, dict, and collections.deque, the expression x in y is functionally equivalent to any(x is e or x == e for e in y).
Important Considerations: When comparing strings or numbers, always opt for “==” over “is”. When checking if an object is None, using either is or == is often equivalent as None is a singleton. However, is is generally preferred stylistically. While some classes might implement __eq__ to make comparisons with None return True, it’s uncommon. In such cases, using == is recommended unless you want to bypass this atypical behavior.
For insights into sort comparisons using >, <, >=, <=, refer to .
Language Comparisons: In Java, identity is checked with the == operator, while equality is checked using Object.equals (which can be overridden). JavaScript distinguishes between the identity operator === and the equality operator ==. However, since JavaScript lacks operator overloading, customizing equality is not possible. Instead, equality is determined by the language’s algorithms, which are thoroughly explained here.