The Python is operator is an identity operator that checks whether two variables point to the same object in memory. Unlike the == operator which compares values for equality, is compares object identity by checking if two references point to the exact same memory location. This distinction is crucial for writing correct and efficient Python code.
IS vs == Operator
The key difference between is and == is that == compares values (equality) while is compares identity (same object in memory).
Example 1: Basic Difference
# Value comparison with ==
a = [1, 2, 3]
b = [1, 2, 3]
print(a == b) # Output: True (same values)
print(a is b) # Output: False (different objects)
# Identity comparison
c = a
print(a is c) # Output: True (same object)
# Memory locations
print(f"id(a): {id(a)}")
print(f"id(b): {id(b)}")
print(f"id(c): {id(c)}") True False True id(a): 140234567890 id(b): 140234567920 id(c): 140234567890
The == operator returns True because both lists have the same content, but is returns False because they are separate objects in memory.
Understanding Object Identity with id()
The id() function returns the unique identifier (memory address) of an object, which the is operator uses for comparison.
Example: Using id() Function
x = [1, 2, 3, 4, 5]
y = [1, 2, 3, 4, 5]
z = x
# Check identity with 'is'
print(x is z) # Output: True
print(x is y) # Output: False
# Verify with id()
print(f"id(x): {id(x)}")
print(f"id(y): {id(y)}")
print(f"id(z): {id(z)}")
# x and z have the same id
# y has a different id True False id(x): 140234567890 id(y): 140234567920 id(z): 140234567890
The id() function returns the unique identifier (memory address) of an object, which the is operator uses for comparison.
IS NOT Operator
The is not operator returns True when two variables refer to different objects in memory.
Example: Using is not
a = [1, 2, 3, 4, 5]
b = [1, 2, 3, 4, 5]
c = a
# Using 'is not'
print(a is not c) # Output: False (they are the same object)
print(a is not b) # Output: True (different objects)
# Equivalent to: not (a is b)
print(not (a is b)) # Output: True False True True
Operator Comparison Table:
| Expression | Meaning | Use Case |
|---|---|---|
a is b | Same object in memory | Check object identity |
a is not b | Different objects in memory | Check object difference |
a == b | Equal values | Check value equality |
a != b | Different values | Check value inequality |
When to Use IS Operator
The is operator should be used in specific scenarios where object identity matters more than value equality.
Use Case 1: Checking for None
def process_data(value):
# CORRECT: Always use 'is' with None
if value is None:
print("No data provided")
return
print(f"Processing: {value}")
# WRONG: Don't use == with None
def wrong_check(value):
if value == None: # Not Pythonic
print("This works but is not recommended")
process_data(None) # Output: No data provided
process_data(42) # Output: Processing: 42 No data provided Processing: 42
Always use is or is not when comparing with None, as it's more explicit and faster.
Use Case 2: Checking for Boolean Singletons
def check_flag(flag):
# Use 'is' for True/False when checking identity
if flag is True:
print("Flag is explicitly True")
elif flag is False:
print("Flag is explicitly False")
else:
print(f"Flag is truthy/falsy but not boolean: {flag}")
check_flag(True) # Flag is explicitly True
check_flag(1) # Flag is truthy/falsy but not boolean: 1
check_flag(False) # Flag is explicitly False
check_flag(0) # Flag is truthy/falsy but not boolean: 0 Flag is explicitly True Flag is truthy/falsy but not boolean: 1 Flag is explicitly False Flag is truthy/falsy but not boolean: 0
Use Case 3: Checking if Variables Reference Same List/Dict
def modify_list(original_list, new_list):
if original_list is new_list:
print("Warning: Same list reference, modifications will affect both")
return False
return True
my_list = [1, 2, 3]
same_ref = my_list
different_list = [1, 2, 3]
modify_list(my_list, same_ref) # Warning message
modify_list(my_list, different_list) # Returns True Warning: Same list reference, modifications will affect both
Python's Integer and String Interning
Python automatically interns small integers and some strings as an optimization, which can lead to surprising is behavior.
Example: Small Integer Caching
# Small integers (-5 to 256) are cached
a = 256
b = 256
print(a is b) # Output: True
a = 257
b = 257
print(a is b) # Output: False (usually, depends on implementation)
# This is due to Python's integer interning optimization
print(id(256) == id(256)) # True
print(id(257) == id(257)) # May vary True False True True
Example: String Interning
# String interning
a = "TutorialsPoint"
b = a
print(f"id(a), id(b): {id(a)}, {id(b)}")
print(f"a is b: {a is b}") # Output: True
print(f"b is not a: {b is not a}") # Output: False
# Identical string literals are often interned
x = "hello"
y = "hello"
print(x is y) # Output: True (usually)
# But not always for dynamically created strings
x = "hello world"
y = "hello world"
print(x is y) # Output: May be True or False
# Strings with spaces/special chars may not be interned
x = "hello world!"
y = "hello world!"
print(x is y) # Output: False (typically) id(a), id(b): 140234567890, 140234567890 a is b: True b is not a: False True True False
Python automatically interns small integers and some strings as an optimization, which can lead to surprising is behavior. Never rely on this behavior in production code.
Common Pitfalls and Mistakes
Understanding common mistakes when using the is operator helps avoid bugs and write more reliable code.
Mistake 1: Using IS for Value Comparison
# WRONG: Using 'is' to compare values
a = 1000
b = 1000
if a is b: # Unreliable!
print("Equal")
else:
print("Not equal") # Usually prints this
# CORRECT: Use == for value comparison
if a == b: # Reliable
print("Equal") # Always prints this for equal values Mistake 2: Relying on Integer Interning
# Don't rely on this behavior!
def bad_comparison(x, y):
if x is y: # Only works reliably for small integers
return True
return False
print(bad_comparison(5, 5)) # True (cached)
print(bad_comparison(500, 500)) # False (not cached)
# CORRECT approach
def good_comparison(x, y):
if x == y: # Always reliable
return True
return False
print(good_comparison(5, 5)) # True
print(good_comparison(500, 500)) # True Mistake 3: Misunderstanding Mutable vs Immutable Types
# Immutable types (int, str, tuple)
a = 42
b = 42
print(a is b) # May be True (depends on interning)
# Mutable types (list, dict, set)
x = [1, 2]
y = [1, 2]
print(x is y) # Always False (different objects)
# Assignment creates reference, not copy
z = x
print(x is z) # True (same object)
# Use copy to create new object
import copy
w = copy.copy(x)
print(x is w) # False (different objects)
print(x == w) # True (same values) Best Practices
Following best practices when using the is operator ensures reliable and Pythonic code.
Practice 1: Always Use IS with None
# GOOD: Checking for None
if value is None:
handle_none()
# BAD: Using == with None
if value == None: # Works but not Pythonic
handle_none() Always use <code>is</code> and <code>is not</code> when comparing with <code>None</code>.
Practice 2: Use == for Value Comparison
# GOOD: Value comparison
if count == 0:
print("Empty")
# BAD: Using 'is' for value comparison
if count is 0: # Unreliable!
print("Empty") Use <code>==</code> for value comparisons in most other cases.
Practice 3: Use IS for Singleton Objects
# GOOD: Checking if same list
if original_list is modified_list:
print("Same object")
# GOOD: Checking if equal values
if list1 == list2:
print("Equal contents") Use <code>is</code> for singleton objects (<code>None</code>, <code>True</code>, <code>False</code>) and to check if two variables reference the same mutable object.
Practice 4: Never Rely on Interning
# BAD: Relying on integer interning
if x is 256: # May work, but unreliable
do_something()
# GOOD: Use == for value comparison
if x == 256: # Always reliable
do_something() Never rely on integer or string interning in production code.
Real-World Examples
These examples demonstrate practical uses of the is operator in real-world scenarios.
Example 1: Singleton Pattern Implementation
class DatabaseConnection:
_instance = None
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
print("Creating new database connection")
return cls._instance
# Test singleton
db1 = DatabaseConnection()
db2 = DatabaseConnection()
print(db1 is db2) # Output: True (same instance)
print(id(db1) == id(db2)) # Output: True Creating new database connection True True
Example 2: Cache Implementation
class Cache:
def __init__(self):
self._cache = {}
self._MISSING = object()
def get(self, key, default=None):
result = self._cache.get(key, self._MISSING)
if result is self._MISSING:
print(f"Cache miss for key: {key}")
return default
print(f"Cache hit for key: {key}")
return result
def set(self, key, value):
self._cache[key] = value
# Usage
cache = Cache()
cache.set('user_1', {'name': 'Alice'})
cache.get('user_1') # Cache hit
cache.get('user_2') # Cache miss
cache.get('user_2', {}) # Cache miss, returns {} Cache hit for key: user_1 Cache miss for key: user_2 Cache miss for key: user_2
Example 3: Defensive Copy Detection
def modify_list_safely(original, new_items):
# Check if caller passed the same list
if original is new_items:
raise ValueError("Cannot pass same list for both arguments")
original.extend(new_items)
return original
my_list = [1, 2, 3]
additional = [4, 5]
result = modify_list_safely(my_list, additional)
print(result) # [1, 2, 3, 4, 5]
try:
modify_list_safely(my_list, my_list) # Raises ValueError
except ValueError as e:
print(f"Error: {e}") [1, 2, 3, 4, 5] Error: Cannot pass same list for both arguments
Try it Yourself
Practice what you've learned by modifying the code below. Try changing the values and conditions to see different outputs!
// Click "Run Code" to see results
Related Topics
Frequently Asked Questions
What is the difference between 'not' and '!' in Python?
Python uses the keyword not for logical NOT operations. The symbol ! is used in other languages like C, Java, and JavaScript, but it will cause a SyntaxError in Python.
What is the difference between 'not' and 'not in'?
The not operator is a logical operator that negates boolean values. The not in operator is a membership operator that checks if an element is not in a sequence. They serve different purposes.
What is the precedence of 'not' compared to 'and' and 'or'?
The not operator has the highest precedence among logical operators, followed by and, then or. This means not a and b is evaluated as (not a) and b.
Should I use 'if not x:' or 'if x == False:'?
Prefer if not x: over if x == False:. The first checks for falsy values (None, 0, empty strings, etc.), while the second only checks for the boolean False value.
Can I use 'not' with non-boolean values?
Yes. Python evaluates values in boolean context. not converts the operand to boolean first, then negates it. Falsy values (0, None, empty collections) become True when negated.
How do I toggle a boolean value with 'not'?
You can use toggle = not toggle to flip a boolean value. This is a common pattern for alternating actions in loops or state management.