Skip to content

What is the difference between != and is not in Python

Learn how Python's != (value comparison) differs from is not (identity comparison) and when to use each for accurate object checks.

These two operators serve fundamentally different purposes: != compares whether values are different, while is not checks whether two references point to different objects in memory. Choosing the wrong one can lead to subtle bugs.

Core Differences

Operator Checks Question It Answers
!= Value/Content Do these have different data?
is not Identity/Memory Are these different objects?

Value Comparison with !=

The != operator compares the actual content of objects:

list1 = [1, 2, 3]
list2 = [1, 2, 3]

print(list1 != list2)  # False (same content)
print(list1 == list2)  # True (same content)

list3 = [1, 2, 4]
print(list1 != list3)  # True (different content)

How != Works

Python calls the __ne__ (not equal) method when you use !=:

class Product:
    def __init__(self, name, price):
        self.name = name
        self.price = price
    
    def __eq__(self, other):
        if not isinstance(other, Product):
            return NotImplemented
        return self.name == other.name and self.price == other.price
    
    def __ne__(self, other):
        return not self.__eq__(other)

product1 = Product("Widget", 9.99)
product2 = Product("Widget", 9.99)
product3 = Product("Gadget", 19.99)

print(product1 != product2)  # False (same values)
print(product1 != product3)  # True (different values)

Identity Comparison with is not

The is not operator checks whether two references point to different memory locations:

list1 = [1, 2, 3]
list2 = [1, 2, 3]
list3 = list1  # Same reference

print(list1 is not list2)           # True (different objects)

print(list1 is not list3)           # False (same object)

print(f"list1 id: {id(list1)}")
print(f"list2 id: {id(list2)}")     # Different from list1
print(f"list3 id: {id(list3)}")     # Same as list1

Output:

True
False
list1 id: 140077478709696
list2 id: 140077477528128
list3 id: 140077478709696

Visualizing the Difference

a = [1, 2, 3]
b = [1, 2, 3]

print(a != b)       # False (content is equal)
print(a is not b)   # True (different memory locations)


c = a

print(a != c)       # False (content is equal)
print(a is not c)   # False (same memory location)

The None Special Case

Always use is not when checking for None:

def fetch_user(user_id):
    users = {1: "Alice", 2: "Bob"}
    return users.get(user_id)

result = fetch_user(999)

if result is not None:
    print(f"Found: {result}")
else:
    print("User not found")

if result != None:
    print(f"Found: {result}")

Why is not None Matters

Some objects can incorrectly compare equal to None:

class TrickyObject:
    def __eq__(self, other):
        return True  # Equals everything!

obj = TrickyObject()

print(obj != None)          # False (comparison says they're "equal")
print(obj is not None)      # True (but they're different objects!)

if obj is not None:
    print("Object exists")  # This correctly executes

Output:

False
True
Object exists

Integer Caching Gotcha

Python caches small integers, which can cause confusing is behavior:

a = 100
b = 100
print(a is not b)  # False (same cached object!)

x = 1000
y = 1000
print(x is not y)  # True (different objects)

print(x != y)      # False (same value)

Output:

False
False
False

String Interning

Similar caching behavior exists for strings:

s1 = "hello"
s2 = "hello"
print(s1 is not s2)  # False (same interned object)

s3 = "hello world"
s4 = "hello world"
print(s3 is not s4)  # May be True or False!

print(s3 != s4)      # Reliable: False

Output:

False
False
False

Practical Examples

Checking Optional Parameters

def greet(name=None):
    if name is not None:
        print(f"Hello, {name}!")
    else:
        print("Hello, stranger!")

greet("Alice")  # Hello, Alice!
greet()         # Hello, stranger!

Output:

Hello, Alice!
Hello, stranger!

Validating Return Values

def find_index(items, target):
    """Return index of target, or None if not found."""
    for i, item in enumerate(items):
        if item == target:
            return i
    return None

result = find_index([1, 2, 3], 2)

if result is not None:
    print(f"Found at index {result}")

Output:

Found at index 1

Comparing Objects

user_input = [1, 2, 3]
expected = [1, 2, 3]

if user_input != expected:
    print("Input doesn't match expected values")
else:
    print("Values match!")  # This prints

Output:

Values match!

Common Mistakes

name1 = "Alice"
name2 = "Alice"
if name1 is not name2:  # Unreliable!
    print("Different names")

if name1 != name2:
    print("Different names")

value = None
if value != None:       # Works but not Pythonic
    process(value)

if value is not None:
    process(value)

Quick Reference

Scenario Correct Operator Example
Compare numbers != if count != 0:
Compare strings != if name != "admin":
Compare lists/dicts != if data != expected:
Check for None is not if result is not None:
Check same object is not if obj1 is not obj2:

Summary

  • Use != for comparing values-numbers, strings, lists, or any data content.
  • Use is not exclusively for checking None or when you specifically need to verify that two variables reference different objects in memory.

Following this rule prevents subtle bugs and keeps your code Pythonic.

Navigation