The Bug That Wastes Hours
Here's a debugging horror story I've seen play out dozens of times: a developer spends 30 minutes tracking down a bug, only to discover that their "equal" lists aren't actually equal—at least not in the way Python sees it.
The culprit? Confusing is with ==.
This single misunderstanding causes more late-night debugging sessions than almost any other Python concept. Let's fix that forever.
Prerequisites
Before diving in, you should understand:
- Basic Python syntax
- What variables are and how assignment works
- The concept of objects in Python
The Core Distinction
Python gives you two ways to check if things are "the same":
| Operator | Checks | Question it answers |
|---|---|---|
== |
Value | Do these have the same content? |
is |
Identity | Are these literally the same object in memory? |
Let's see the trap in action:
a = [1, 2, 3]
b = [1, 2, 3]
print(a == b) # True - same values
print(a is b) # False - different objects!
Both lists contain [1, 2, 3], so they're equal in value. But Python created two separate list objects at two different memory addresses. They're twins, not the same person.
When This Actually Breaks Your Code
Here's a real-world scenario. Imagine you're caching API responses:
cached_result = None
def get_data():
global cached_result
if cached_result is []: # BUG! This will never be True
return cached_result
# ... fetch data ...
That is [] check will always fail because each [] creates a new list object. The condition should use == or, better yet, check for truthiness.
The Rule That Will Save You
Memorize this:
| Use Case | Operator |
|---|---|
Checking for None
|
is |
| Everything else | == |
# Correct: use 'is' for None
if result is None:
handle_missing_data()
# Correct: use '==' for values
if user_input == "quit":
break
# Correct: use '==' for list comparison
if current_list == expected_list:
print("Match!")
Why is for None?
None is special in Python—it's a singleton. There's only ever one None object in the entire Python runtime. When you write None, you're always referring to that exact same object.
This means is None is:
- Semantically correct - You're asking "is this the None object?"
- Slightly faster - Identity checks are quicker than equality checks
- PEP 8 recommended - It's the official Python style guide's preference
Bonus: Short-Circuit Evaluation
While we're talking about operators, here's another powerful concept. Python is lazy—it won't evaluate what it doesn't need to:
x = 0
result = x != 0 and (10 / x) > 5
print(result) # False, no ZeroDivisionError!
Python sees that x != 0 is False. Since False and anything is always False, it skips the division entirely.
You can use this intentionally:
# Clean default value pattern
username = input("Name: ") or "Guest"
If the user enters nothing (empty string is falsy), Python short-circuits and uses "Guest".
Common Mistakes to Avoid
-
Using
isto compare strings or numbers - It might work sometimes due to Python's caching, but it's unreliable and wrong -
Using
isto compare lists or dicts - Almost always a bug -
Forgetting that
is notexists - Useif x is not None:instead ofif not x is None: -
Assuming
==andisare interchangeable - They fundamentally check different things
Quick Reference
# IDENTITY (is) - Same object?
x is None # ✓ Correct
x is True # ✓ Correct (True is also a singleton)
a is b # Rarely what you want for other types
# EQUALITY (==) - Same value?
name == "Alice" # ✓ Correct
count == 0 # ✓ Correct
list1 == list2 # ✓ Correct
Key Takeaways
-
==checks value (do these contain the same data?) -
ischecks identity (are these the exact same object?) - Use
isforNonechecks,==for everything else - Short-circuit evaluation with
and/orcan prevent errors and create elegant defaults - When in doubt, use
==—it's almost always what you want
What's Next?
Understanding the difference between identity and equality is fundamental to writing correct Python code. It's one of those concepts that separates beginners from practitioners.
This article is adapted from "Zero to AI Engineer: Python Foundations." I'm writing this book in public—subscribe on Substack to follow along and get notified at launch.
If this helped you, drop a 🧡 and share it with someone learning Python!
Top comments (2)
Really clear and beginner friendly article — thank you!
Thank you! That's exactly the goal—making these "gotcha" concepts click before they cause debugging headaches. Glad it landed well. 🙏