Back to Blog
Python

Python == vs is: When to Use Each

python == vs is: Learn the difference between Python's == and is operators, when to use each for correct behavior, and how they affect performance and readability.

Python equalityobject identityPython operatorsNone comparisonPython internals
Two Python objects compared with == and is, showing value vs identity

In Python, the difference between == and is is a common source of confusion for developers coming from other languages. python == vs is is not just a syntax trivia question; it directly affects correctness, performance, and maintainability. The two operators answer different questions: == checks whether two objects have the same value, while is checks whether two references point to the exact same object in memory. Understanding this distinction is essential for writing predictable code, especially when dealing with mutable objects, singletons, and performance-sensitive paths.

What == Compares: Value Equality

The == operator invokes the __eq__ method of the left operand, which by default compares object identity unless overridden. For built-in types like integers, strings, lists, and dictionaries, == performs a deep value comparison. For example:

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

Here, a and b are distinct list objects, but their contents are identical, so == returns True. This is the behavior most developers expect when comparing data. For custom classes, == defaults to identity unless you implement __eq__. If you define a class and do not override __eq__, two instances with the same attributes will not be considered equal:

class Point: def __init__(self, x, y): self.x = x self.y = y p1 = Point(1, 2) p2 = Point(1, 2) print(p1 == p2) # False

To make == compare values, you must implement __eq__ (and usually __hash__ if the object is used in sets or as dictionary keys).

What is Compares: Object Identity

The is operator checks whether two references point to the same object in memory. It does not call any comparison method; it is a direct pointer comparison. For example:

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

b is an alias for the same list object, so is returns True. If you create two separate lists with identical contents, is returns False:

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

This behavior is fundamental to how Python manages memory. The is operator is also used to check for None, which is a singleton object. The canonical way to test for None is if x is None: rather than if x == None:. The latter works in most cases because None is a singleton and == falls back to identity, but it is less explicit and can be misleading if a custom class overrides __eq__ to treat None as equal.

When to Use is for Singletons and Interned Objects

Python guarantees that certain objects are singletons: None, True, False, and NotImplemented. For these, is is the idiomatic and recommended comparison. For example:

if result is None: # handle missing result

Using is for these constants is faster and clearly communicates intent. The same applies to True and False when checking boolean values, though == also works. However, for other objects, is should be used only when you explicitly need to know whether two variables refer to the same object, such as when checking if a function argument is the default sentinel or when implementing caching.

Python also interns certain immutable objects, like small integers (typically -5 to 256) and some strings. Interning means the interpreter reuses the same object for equal values, so is may return True for two separately created integers in that range:

a = 256 b = 256 print(a is b) # True (due to interning) c = 257 d = 257 print(c is d) # False (outside interned range)

This behavior is an implementation detail and should not be relied upon. Using is for integer comparison is fragile and can break across Python versions or implementations. Always use == for numeric and string value comparisons unless you are certain about interning and the code is performance-critical in a controlled environment.

Common Pitfalls with Mutable Objects and Chained Comparisons

A frequent mistake is using is to compare values that are not guaranteed to be interned. For example, comparing two strings that are constructed dynamically:

def get_name(): return ''.join(['a', 'b']) name1 = get_name() name2 = 'ab' print(name1 is name2) # False in CPython (usually) print(name1 == name2) # True

The is result here is not guaranteed; it depends on whether the interpreter interns the string. Relying on it leads to subtle bugs. Another pitfall is using is with mutable objects where you actually want value equality. For instance, comparing two lists with is will almost always be False because they are distinct objects, even if they contain the same elements.

Chained comparisons can also confuse the issue. Python evaluates a == b == c as (a == b) and (b == c), but if you write a is b is c, it means (a is b) and (b is c). While this is logically consistent, it is rarely what you intend when comparing values. Prefer explicit and for clarity.

Performance Considerations: is vs ==

The is operator is generally faster than == because it does not invoke any method calls or traverse object structures. It is a direct memory address comparison. In performance-sensitive code, especially inside tight loops, using is for singleton checks like None can reduce overhead. For example:

if value is None: # fast path

vs.

if value == None: # slower, calls __eq__

For custom objects, == may trigger a deep comparison that walks nested attributes, which can be expensive. If you only need to know whether two references point to the same object, is is the right tool. However, the performance difference is usually negligible unless you are comparing millions of times. Do not sacrifice correctness for micro-optimizations. Use is when identity is the semantic requirement, not merely for speed.

Best Practices for Readable and Maintainable Code

The clearest rule is: use is for None, True, False, and other documented singletons; use == for everything else. This aligns with the Python style guide (PEP 8) and makes your intent obvious. For custom classes, implement __eq__ when you want value equality, and always pair it with __hash__ if the objects are used in sets or as dictionary keys. Avoid using is for numeric or string comparisons unless you have a specific, well-documented reason, such as checking against a sentinel object you created yourself.

Another best practice is to be consistent within a codebase. If you mix == and is for the same kind of comparison, readers will not know whether you intended identity or value equality. When reviewing code, treat is as a strong signal that the author wants to check object identity, and verify that the operands are indeed singletons or intentionally shared references.

Understanding Interning and Its Limits

Interning of immutable objects is an optimization that can make is return True for equal values. In CPython, small integers in the range -5 to 256 are pre-allocated, and certain short strings are interned. This behavior is not part of the language specification and may differ in other implementations like PyPy or Jython. Relying on interning for correctness is dangerous because it can change with Python version or runtime configuration. If you find yourself writing if x is 5:, you are likely making a mistake. The only safe use of is for non-singleton objects is when you control object creation, such as when you use a factory that returns a cached instance and you want to verify that the same instance is returned.

A more subtle issue arises with the copy module. A shallow copy creates a new object, so original is copy is False, but original == copy may be True if __eq__ is implemented. This is often surprising to developers who assume is and == are interchangeable. Always choose the operator that matches the question you need to answer: "Do these objects hold the same data?" or "Are these the same object?"

When working with None, using is is not just a style preference; it can prevent bugs in code that uses custom classes with a permissive __eq__. For example, a class that defines __eq__ to return True for any argument would make x == None return True, even when x is not None. Using is avoids this trap entirely. This is why linters like flake8 and pylint enforce is for None comparisons.

In summary, the choice between == and is is not about one being better than the other; it is about using the correct tool for the semantic meaning you need. == compares values, is compares identity. For most data comparisons, use ==. For checking against None or other singletons, use is. And when performance matters, is is cheaper, but only use it when identity is the actual requirement. Understanding this distinction will make your Python code more robust, readable, and efficient.

python == vs is: Practical Usage and Code Examples | RYUSLOG DEV