Back to Blog
Python

Python is Operator: Identity vs Equality

python is operator: Learn how Python's `is` operator checks object identity, when to use it instead of `==`, and common pitfalls with interning and singletons.

identity comparisonequalityobject identityPython syntaxsingleton comparison
Illustration of Python is operator comparing two objects by identity, with a scale showing identity vs equality.

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

The is operator in Python compares object identity, not value. Two variables are is-equal only when they reference the same object in memory. This distinction is easy to overlook, and it leads to subtle bugs when developers assume is behaves like ==. Understanding the difference is essential for writing correct, maintainable Python code.

The Difference Between is and ==

The == operator compares values by invoking the __eq__ method of the left operand, which can be overridden in custom classes. The is operator, on the other hand, performs a direct identity check: it returns True only if both operands point to the same object in memory. Consider this minimal example:

a = [1, 2, 3] b = [1, 2, 3] print(a == b) # True, because values are equal print(a is b) # False, because they are different list objects

Even though a and b contain the same elements, they are distinct objects. The is operator correctly reports that they are not the same. This behavior is fundamental to Python's object model and is not something you can override.

How Python's is Works Under the Hood

In CPython, the reference implementation, is compares the memory addresses of the two objects. Every object in Python has a unique identity, which can be retrieved with the built-in id() function. The is operator is essentially a shortcut for id(a) == id(b). Because it relies on pointer comparison, it is a constant-time operation that does not call any user-defined methods.

This means that is is not subject to operator overloading. Even if a class defines __eq__ to make == return False for two identical objects, is will still return True when both variables reference the same instance. This property makes is predictable and safe for identity checks, but also dangerous when used for value comparison.

Common Pitfalls: Integer Interning and String Caching

Python caches small integers in the range -5 to 256. As a result, assignments like a = 256 and b = 256 cause both variables to reference the same cached object, so a is b evaluates to True. Outside this range, integers are created on demand, and a is b becomes False for equal values:

a = 256 b = 256 print(a is b) # True (cached) c = 257 d = 257 print(c is d) # False (separate objects)

String interning is less predictable. CPython interns some strings, such as short identifiers and literals that look like identifiers, but this behavior is not guaranteed by the language specification. Relying on is for string comparison is fragile and can break across Python implementations or versions.

When to Use is in Practice

The primary use case for is is comparing to singletons: None, True, and False. These are guaranteed to have only one instance in any Python interpreter. The recommended idiom is if x is None: rather than if x == None:, because the latter can trigger unintended __eq__ calls and is slower. The same applies to boolean checks: if flag is True: is explicit and avoids any ambiguity with truthiness.

Another legitimate use is checking whether two variables refer to the same object, which is common in caching, memoization, or when implementing data structures that rely on object identity. For example, a linked list node might compare its next attribute to a sentinel object using is to detect the end of the list.

Performance and Runtime Behavior

Because is does not invoke any method lookup or equality logic, it is faster than == in most cases. For simple types like integers, the difference is negligible, but for objects with expensive __eq__ implementations, using is can avoid significant overhead. However, micro-optimizations with is are rarely justified unless you are comparing to None or checking identity in a tight loop.

It is also worth noting that is is always a constant-time operation, while == may have O(n) complexity for containers like lists or dictionaries. If you only need to know whether two variables point to the same object, is is the correct and efficient choice.

Using is with Custom Objects

By default, is compares identity, and you cannot change that behavior. Even if a class defines __eq__ to compare attributes, is will still return False for two distinct instances with identical attribute values. This is by design: identity is a fundamental property of objects, while equality is a domain-specific concept.

class Point: def __init__(self, x, y): self.x = x self.y = y def __eq__(self, other): return self.x == other.x and self.y == other.y p1 = Point(1, 2) p2 = Point(1, 2) print(p1 == p2) # True (uses __eq__) print(p1 is p2) # False (different objects)

If you need to compare values, use ==. If you need to check whether two references point to the same object, use is. Mixing them up can lead to subtle bugs, especially when objects are passed through functions or stored in collections.

Edge Cases and Gotchas

One common mistake is using is to compare strings that are not interned. For example, a = "hello" and b = "hello" may or may not be the same object depending on how the strings are created. Concatenation or formatting often produces new objects, making a is b unreliable. Always use == for string value comparison.

Another gotcha is the is not operator, which is the negation of is. It is often clearer to write if x is not None: than if not (x is None):. Both are correct, but the former reads more naturally.

Finally, be cautious when using is with mutable objects that are shared across parts of your program. If you rely on identity to distinguish objects, make sure you are not accidentally comparing to a temporary object that gets garbage-collected. The is operator does not keep objects alive; it only checks the current reference.

Understanding the python is operator is a small but critical part of Python's object model. Using it correctly prevents bugs and makes your code more readable. When in doubt, choose == for value comparison and reserve is for identity checks with singletons or explicit object references.

python is operator: Practical Usage and Code Examples | RYUSLOG DEV