Back to Blog
Python

Python Identity vs Equality: Using is and == Correctly

python identity equality: Understand the difference between Python's identity and equality checks, when to use is vs ==, and common pitfalls with object comparison.

pythonidentityequalityis operator== operatorobject comparison
Illustration showing two objects with same value but different identity, and the is vs == comparison in Python.

python identity equality requires a clear understanding of the core syntax, runtime behavior, and practical implementation patterns demonstrated in the examples below.

The Difference Between is and ==

In Python, == compares values, while is compares object identity. Two objects can have the same value but be different objects in memory. For example:

a = [1, 2, 3] b = [1, 2, 3] print(a == b) # True print(a is b) # False

The list a and list b contain the same elements, so they are equal. But they are distinct objects, each with its own memory location, so a is b is False. Understanding this distinction is central to python identity equality and prevents subtle bugs when comparing objects.

How Python Determines Identity

Every object in Python has an identity, which can be obtained with the built-in id() function. The identity is an integer that is guaranteed to be unique and constant for the object during its lifetime. In CPython, id() returns the memory address of the object, but that is an implementation detail; the language only guarantees uniqueness.

x = [1, 2] y = x print(id(x) == id(y)) # True, same object

The is operator is equivalent to comparing id() values. It checks whether two references point to the same object. This is faster than == because it does not invoke any comparison logic; it only compares the memory addresses.

When to Use Identity Checks

Identity checks are appropriate when you care about object identity rather than value. The most common case is comparing to None. Because None is a singleton, x is None is the idiomatic way to test for it, and it is faster and more explicit than x == None.

Another use case is comparing to a sentinel value that you create as a unique object:

_MISSING = object() def get_value(data, key): value = data.get(key, _MISSING) if value is _MISSING: raise KeyError(key) return value

Here, is ensures that no actual value in the dictionary can accidentally match the sentinel, because the sentinel is a unique object.

Equality and Custom Objects

When you define a class, the default behavior of == is to compare identity, just like is. To customize equality, you override __eq__. If you do, you should also override __hash__ to maintain consistency, because objects that compare equal must have the same hash value.

class Point: def __init__(self, x, y): self.x = x self.y = y def __eq__(self, other): if not isinstance(other, Point): return NotImplemented return self.x == other.x and self.y == other.y def __hash__(self): return hash((self.x, self.y))

Now two Point instances with the same coordinates compare equal, but they remain distinct objects. This is useful for value objects where equality is based on content.

Common Pitfalls: Integer Caching and String Interning

Python caches small integers (typically -5 to 256) and may reuse them, so a is b can be True for those integers even if they are assigned separately. This is an implementation detail and should not be relied upon. For example:

a = 256 b = 256 print(a is b) # True in CPython c = 257 d = 257 print(c is d) # False, likely

Similarly, string interning can make is return True for some strings that are created from literals, but not for dynamically constructed strings. Never use is for value comparison of numbers or strings; always use ==.

Performance and Maintainability Considerations

Using is is slightly faster than == because it avoids method dispatch and comparison logic. However, the difference is negligible in most applications. The real benefit is clarity: x is None clearly expresses that you are checking for the singleton None, not for an object that happens to compare equal to None.

Maintainability suffers when developers use == for identity checks or is for value checks. Code that relies on implementation-specific behavior like integer caching is fragile and may break on a different Python interpreter or version. Stick to the semantic meaning: use is for identity, == for equality.

python identity equality: Practical Usage and Code Examples | RYUSLOG DEV