Meet Francis, a money lender and a skilled programmer with a unique approach to keeping track of his borrowers. Instead of noting down names, he stores their year of birth and the amount they owe him in a Python dictionary. Despite this peculiar method, he has never mistakenly collected from the wrong person. However, the way he retrieves his money is a story for another day.

The Case of Alice and Bob
Alice borrowed $300 from Francis, and she was born in 2001. Francis records this in his code:
alice = {'born': 2001, 'debt': 300}
Bob, Alice’s twin brother, also borrows the same amount. Francis notes this down as well:
bob = {'born': 2001, 'debt': 300}
Although Alice and Bob have the same birth year and debt, they are not the same person. In Python, if Francis wants to check if the data for Alice and Bob is the same, he uses the ==
operator:
alice == bob
and we would get the following as the output.
True
However, to confirm that Alice and Bob are indeed different individuals, he uses the “is” operator:
alice is bob
which correctly outputs:
False
Alice’s Disguise
Alice, in an attempt to escape her debt, decides to change her name to Malice. But Francis is an old player in this game. He updates his records:
malice = alice
As explained in a previous post, variables in Python are like sticky notes pointing to objects. Now, both alice
and malice
reference the same dictionary object.
Correct way to visualize variables in Python
If you’ve ever taken a beginner programming course, you’ve probably encountered the concept of variables. And if you were taught to think of variables as “boxes” containing values or objects, it’s time for a rethink. That classic box metaphor might actually be leading you astray. Instead, picture variables as sticky notes — here’s why this metaphor make…
If we check the content of malice
:
malice
we get
{'born': 2001, 'debt': 300}
So what would we get if we typed:
alice == malice
Well as the content in alice and malice both is the same, we would get
True
But now because alice and malice both are pointing to the same object and therefore have the same identity, we would even get True for
alice is malice
True
We could further check this by checking the id of all of them
id(alice)
id(malice)
id(bob)
Bad luck for Alice. Can’t run from Francis.
The Difference Between ==
and is
The ==
operator compares the values of objects, while the is
operator checks their identities. In most programming scenarios, we are more concerned with the values, hence ==
is more commonly used. However, there are specific cases where is
is more appropriate.
Using "is”
with Singletons
A common use of is
is comparing a variable to a singleton, such as None
:
x is None
And the proper way to write its negation is:
x is not None
Sentinels and Performance
None
is the most frequent singleton checked with is
. Another example involves sentinel objects, unique markers used in certain coding patterns:
END_OF_DATA = object()
def traverse(...):
if node is END_OF_DATA:
return
# Additional code
The is
operator is faster than ==
because it directly compares memory addresses without invoking special methods. While a == b
translates to a.__eq__(b)
, which can be computationally expensive, a is b
simply compares their IDs. Built-in types often override __eq__
to provide meaningful equality checks based on their attributes, which might involve significant processing, especially for large or nested structures.
Conclusion
In summary, while object equality (==
) is generally more important in programming, identity checks (is
) are crucial when dealing with singletons or specific performance considerations. When in doubt, use ==
, as it usually aligns with your intentions and works well even with None
, albeit slightly less efficiently.
By grasping the distinction between ==
and is
, we can write clearer, more efficient Python code, and maybe even avoid the pitfalls that caught Alice (or Malice) in her tracks.
[1] L. Ramalho, Fluent Python: Clear, Concise, and Effective Programming, O’Reilly Media, 2022.