Skip to main content
The == operator is Python’s value equality operator. It evaluates whether the internal data or state of two objects is equivalent, regardless of whether they occupy the same memory address.

Internal Mechanics

When the == operator is invoked, Python translates the expression into a method call to the __eq__() dunder (magic) method. In Python’s data model, dunder methods are always looked up on the class, not the instance, to bypass instance dictionaries. Therefore, the internal lookup is conceptually type(operand_a).__eq__(operand_a, operand_b). The evaluation follows a strict dispatch mechanism that accounts for inheritance:
  1. Subclass Priority Rule: If operand_b is an instance of a strict subclass of operand_a’s type, Python reverses the standard dispatch order. It will call type(operand_b).__eq__(operand_b, operand_a) first. This ensures that subclasses can properly override and dictate the equality behavior of their parent classes.
  2. Primary Dispatch: If the subclass rule does not apply, Python attempts to call the __eq__ method of the left operand: type(operand_a).__eq__(operand_a, operand_b).
  3. Fallback Dispatch: If the first method called returns the built-in NotImplemented constant (indicating it does not know how to compare itself to the other type), Python falls back to the other operand’s __eq__ method.
  4. Identity Fallback: If both operands return NotImplemented, Python falls back to comparing object identity, effectively evaluating operand_a is operand_b (comparing their memory addresses).

Type Handling and Coercion

Python is strongly typed, meaning == does not perform implicit type coercion across disparate data types (e.g., a string "1" will never equal an integer 1). However, the operator implements specific behaviors for certain built-in types:
  • Numeric Types: Python’s numeric types (int, float, complex, decimal, fractions) implement cross-type equality. The == operator will evaluate 1 == 1.0 as True because their mathematical values are equivalent, despite differing memory representations.
  • Collections: For built-in container types (like list, tuple, dict, and set), the == operator performs a deep, recursive equality check. It iterates through the collections, invoking the == operator on each corresponding pair of elements. Both the structure and the values must match.
  • Custom Objects: By default, user-defined classes inherit their __eq__ method from the base object class. The default implementation strictly compares memory addresses. To achieve value equality in custom objects, the developer must explicitly override the __eq__ method to define the specific attributes that constitute equality.

Hashing Implications

In Python’s data model, the == operator is tightly coupled with the __hash__() method. If two objects evaluate as equal via the == operator (a == b is True), they must yield the exact same integer value when passed to the hash() function, provided they are hashable. If a custom class overrides __eq__ to provide value equality, Python automatically sets its __hash__ to None, making the object unhashable unless __hash__ is also explicitly overridden.
Tired of Poor Python Skills? Fix That With Deep Grasping!Learn More