The One About the £5 Note and the Trip to the Coffee Shop • The Difference Between `is` and `==` in Python
Many misunderstand the `is` keyword in Python and use it in situations when `==` is more appropriate. Let's explore this through a recent trip to a coffee shop with a friend.
I met a friend in a coffee shop the other day. One of those small, quaint cafés with comfy armchairs and books on the walls that you can pick up and read–the books, not the walls–while sipping your hot beverage of choice.
I spotted James on the way to the coffee shop, so we walked the final stretch together, chatting about our journeys from home. We went in and ordered our two coffees. We both ordered a macchiato, if you want to know.
The total cost for the two coffees was £5. I took out my wallet and pulled out a £5 note. But James had also fetched a £5 note from his pocket.
It's that awkward "I'll pay, you'll pay" moment. Let's pick the story up from this point.
Paid subscribers on The Python Coding Stack—this publication—now also get full access to the members’ forum at The Python Coding Place.
You can also explore full membership at The Python Coding Place to access all the video courses.
Thank you to The Python Coding Place for sponsoring this publication.
Paying the Barista
The barista is waiting patiently. She doesn't care which £5 note we give her as long as we give her one of them. The queue is getting quite long now.
From the barista's point of view, all that matters is that payment == 5
returns True
.
So, I let James pay!
The barista cares about the note's value, not whether it came from my pocket or James's. Both notes have the same value.
By the way, I rarely comment about linguistic differences between English variants. But if you prefer, you can convert the pound sterling sign £ to whatever currency symbol you prefer, replace queue with line, and replace note with bill. Here in the UK, the bill is what you get at the end of a meal to show how much you need to pay, which others call the check. In the UK, a cheque is the now-obsolete piece of paper you could use to pay someone.
Aren't you glad programming languages don't have these ambiguities and quirks?!
And while we're here, if you want to quibble about the 's in James's, leave a comment, and we'll discuss it in the comments section.
Picking up the £5 Note From the Floor
I mentioned that a queue was building up by the time James and I got our coffees and paid. But I didn't tell you why. Let's go back a few minutes and resume the story from there.
As we're in the queue, waiting to order, we spot a £5 note on the floor next to our feet. We both check our wallets. We're both missing a £5 note. 'What are the odds of that?' you're wondering. Just play along for now…
Here's one thing you don't know about us. James and I are both geeks. We keep records of the serial numbers of all the notes we carry with us.
How sad is that?! I know. But bear with me a little longer.
Every note issued by the Bank of England has a unique serial number, and no two notes have the same number. You can see the serial number in two places on the image of the £5 note below. The same will be true for notes of other currencies.
James and I get our phones out to check the serial numbers on our spreadsheet apps. We search for the serial number of the £5 note we picked up from the floor. Aha! I found it. The note is mine.
This is my note. Its value of £5 no longer matters in this case. What matters is that this is the actual physical note I put in my pocket before leaving home. Now what matters is that the following expression that contains the is
operator, note_on_the_floor is stephens_note
, returns True
.
The Difference Between is
and ==
in Python
Let's create a couple of lists and experiment with them. James and I both keep a log of all the coffees we drink in a day–yes, we're both that kind of person:

Are these two lists the same? No, clearly they're not. I've had one more coffee than James, and my list has three coffees, while James's only has two.
These two lists are not the same.
But James just got another espresso from the stall on the train platform:
How about now? Are the two lists the same?
I italicised "the same" each time I used it in the previous paragraphs. What do we really mean when we ask whether the lists are the same?
Do we want to know whether James and I drank the same kinds of coffee today? If that's the case, then yes. The values within the lists are the same and in the same order:
The lists are equal, and you used the equality operator ==
to check for this. Their values are the same.
But James and I didn't drink the same coffee. I had my macchiato, which I enjoyed sip by sip. James had his own macchiato, in a different cup. The liquid he drank wasn't the same liquid I did. It would be practically impossible, and somewhat disgusting, to drink the same cup of coffee.
Back to the two Python lists stephens_coffees
and jamess_coffees
. They're not the same object within the Python program. They're two different lists that have similar items in them. Every object in Python has a unique identity which you can find using the built-in function id()
:
The two lists have different identities, as shown by their "serial numbers", just like the £5 notes.
When you compare two objects using the is
keyword, Python will only return True
if the objects have the same identity–that means that they're the same object:
The identity of each list is not the same. You used the is
keyword to check for this.
I also keep track of everything I eat and drink during the day:
You place the list stephens_coffee
in the new list stephens_food_and_drink
, and you add a second item–a list with food.
What's the output of this?
Is the first element of stephens_food_and_drink
the same object as stephens_coffees
? We know it will be equal as it contains the same items in the same order. Let's find out if it's also the exact same list:
Yes, it is. When you place a list within another list, you don't create a copy of the list. Instead, you place a reference to the same list. I explore this topic further in What's In A Name? and What's In A List—Yes, But What's Really In A Python List if you want to read more.
Also, are you interested in technical writing? You’d like to make your own writing more narrative, more engaging, more memorable? Have a look at Breaking the Rules.
Identity and Equality in User-Defined Classes
Let's dive a bit deeper into the topic of identity and equality by creating your own class:
You create a class called Drink
, which has two data attributes:
.name
.price
Next, you create two instances of this class. You check for equality using ==
and whether the objects are the same object using the keyword is
:
False
False
Both checks return False
. The first False
shows the objects are not equal, but we'll get back to this point later. The second False
confirms that the objects don't have the same identity. They're not the same object!
Both these results are unsurprising. Now, try creating two instances having the same values for both data attributes:
The objects espresso
and another_espresso
have the same values for .name
and .price
. However, both the equality and the identity checks return False
again:
False
False
The identity is not the same as these are two different objects, even though they contain the same information. This is the same as the two £5 notes that James and I had. They're both worth £5, but they're not the same note.
But how about equality?
How Does Python Know When Two Objects Are Equal?
The problem is that your program doesn't know what 'being equal' means for an object of type Drink
. In fact, even people might have a different view on what equality should mean in this case:
Are two drinks equal if they have the same name?
Are two drinks equal if they have the same price?
Are two drinks equal if they have the same name and price?
To define what equality means, you can use the .__eq__()
dunder method for the class. This method should return a Boolean depending on how you decide to define equality for this class.
When Python doesn't know how to deal with equality because there's no .__eq__()
method, as in the examples above, it will check for identity instead. Therefore, for any class that doesn't have an .__eq__()
dunder method, is
and ==
return the same value.
Let's define .__eq__()
and explore the three options listed above.
1. Drinks are equal if they have the same name
Let's define .__eq__()
to check if the objects' .name
data attributes are the same:
You define the .__eq__()
dunder method and return the result of checking for equality between the two objects' .name
attributes.
You also create a fourth instance of Drink
. This is another espresso sold from a way-too-fancy shop that charges £4 for the pleasure.
Note that you're no longer using is
to check whether the identity of these objects is the same. Since they're all different objects, is
will always return False
.
The output from the three equality checks is shown below:
True
False
True
You define the equality of two Drink
objects based on whether they have the same name. So, in this case, espresso
and another_espresso
are equal, but so are espresso
and rip_off_espresso
, as they all have the same name, "Espresso"
. It doesn't matter that one is more expensive than the other two.
But espresso
and macchiato
are not equal as they have different .name
attributes, "Espresso"
and "Macchiato"
.
2. Drinks are equal if they have the same price
Now you can change .__eq__()
to check if the objects' .price
data attributes are the same:
The only change is in the .__eq__()
dunder method. The output from this code now gives:
True
True
False
The three objects, espresso
, another_espresso
, and macchiato
, are all equal now. Even the macchiato is equal to the two espressos as it costs the same amount.
However, rip_off_espresso
is no longer equal to espresso
since it's the price that matters now, not the name.
3. Drinks are equal if they have the same name and price
The last change to .__eq__()
is to check for equality for both .name
and .price
:
On this occasion, only espresso
and another_espresso
are equal as they're the only ones that share both a name and a price:
True
False
False
When you define a class, it's up to you to decide what equality means for the instances of your class. You can then implement the .__eq__()
method to reflect your decision.
When Do You Use is
and When ==
in Python
In most cases, when you need to check whether two objects are the same, you're likely to need to check for equality using ==
and not whether the objects have the same identity. Using is
can lead to unexpected results. Have a look at this example using Python's standard REPL (when using CPython):
You get the expected result when you check for equality using the ==
operator. However, the two examples return different results when checking the identity using is
. This is due to how integers are implemented in CPython, and I won't go into the details here. I merely want to demonstrate the dangers of using is
when you really mean to use ==
.
And if this is not confusing enough, you can try to run the same code above using a script instead of the REPL. You'll need to add calls to print()
to show the results:
When you run this code, you may get True
for all four checks:
True
True
True
True
However, you should not rely on this behaviour of the is
keyword. Check for equality using ==
instead.
Here's another example showing when checking for equality should be used instead of the is
keyword:
When you want to check whether the two numbers are the same, you almost certainly want to know whether the value of the two numbers is the same. In this case, the division returns a float. Therefore, first
cannot be the same object as second
since one is an integer and the other is a float. But they have equal values.
You rarely need to use is
in your programs. However, if you want to know that two objects are the exact same instance, then is
is the keyword you need!
It is also common to use is
when checking whether a value is equal to a singleton object such as None
. A singleton is when a class creates only a single instance. There's only ever one instance of None
in any program, so using is
won't cause any issues.
Final Words
You're now familiar with the difference between is
and ==
in Python, and you know that, in most cases, you'll want to use ==
in your code.
And if you can't recall the difference between is
and ==
, get your wallet and look at any banknote in there to remind you about my little adventure with James in our local coffee shop!
[This article was first published in 2023, in a slightly modified form, on The Python Coding Book blog: https://thepythoncodingbook.com/2023/02/07/understanding-the-difference-between-is-and-in-python/]
Code in this article uses Python 3.13
The code images used in this article are created using Snappify. [Affiliate link]
For more Python resources, you can also visit Real Python—you may even stumble on one of my own articles or courses there!
Also, are you interested in technical writing? You’d like to make your own writing more narrative, more engaging, more memorable? Have a look at Breaking the Rules.
And you can find out more about me at stephengruppetta.com
Further reading related to this article’s topic:
Appendix: Code Blocks
Code Block #1
stephens_coffees = ["Double espresso", "Macchiato", "Espresso"]
jamess_coffees = ["Double espresso", "Macchiato"]
Code Block #2
jamess_coffees.append("Espresso")
stephens_coffees
# ['Double espresso', 'Macchiato', 'Espresso']
jamess_coffees
# ['Double espresso', 'Macchiato', 'Espresso']
Code Block #3
stephens_coffees == jamess_coffees
# True
Code Block #4
id(stephens_coffees)
# 4689761920
id(jamess_coffees)
# 4689190848
Code Block #5
stephens_coffees is jamess_coffees
# False
Code Block #6
stephens_food_and_drink = [stephens_coffees, ["Sandwich", "Pasta"]]
stephens_food_and_drink
# [['Double espresso', 'Macchiato', 'Espresso'], ['Sandwich', 'Pasta']]
Code Block #7
stephens_food_and_drink[0] is stephens_coffees
Code Block #8
stephens_food_and_drink[0] is stephens_coffees
# True
Code Block #9
class Drink:
def __init__(self, name, price):
self.name = name
self.price = price
espresso = Drink("Espresso", 2.2)
macchiato = Drink("Macchiato", 2.2)
print(espresso == macchiato)
print(espresso is macchiato)
Code Block #10
class Drink:
def __init__(self, name, price):
self.name = name
self.price = price
espresso = Drink("Espresso", 2.2)
another_espresso = Drink("Espresso", 2.2)
# macchiato = Drink("Macchiato", 2.2)
print(espresso == another_espresso)
print(espresso is another_espresso)
Code Block #11
class Drink:
def __init__(self, name, price):
self.name = name
self.price = price
def __eq__(self, other):
return self.name == other.name
espresso = Drink("Espresso", 2.2)
another_espresso = Drink("Espresso", 2.2)
macchiato = Drink("Macchiato", 2.2)
rip_off_espresso = Drink("Espresso", 4.0)
print(espresso == another_espresso)
print(espresso == macchiato)
print(espresso == rip_off_espresso)
Code Block #12
class Drink:
def __init__(self, name, price):
self.name = name
self.price = price
def __eq__(self, other):
return self.price == other.price
espresso = Drink("Espresso", 2.2)
another_espresso = Drink("Espresso", 2.2)
macchiato = Drink("Macchiato", 2.2)
rip_off_espresso = Drink("Espresso", 4.0)
print(espresso == another_espresso)
print(espresso == macchiato)
print(espresso == rip_off_espresso)
Code Block #13
class Drink:
def __init__(self, name, price):
self.name = name
self.price = price
def __eq__(self, other):
return (
self.name == other.name
and self.price == other.price
)
espresso = Drink("Espresso", 2.2)
another_espresso = Drink("Espresso", 2.2)
macchiato = Drink("Macchiato", 2.2)
rip_off_espresso = Drink("Espresso", 4.0)
print(espresso == another_espresso)
print(espresso == macchiato)
print(espresso == rip_off_espresso)
Code Block #14
first = 10 + 10
second = 30 - 10
first == second
# True
first is second
# True # <-- This returns True
first = 1000 + 1000
second = 3000 - 1000
first == second
# True
first is second
# False # <-- This returns False
Code Block #15
first = 10 + 10
second = 30 - 10
print(first == second)
print(first is second)
first = 1000 + 1000
second = 3000 - 1000
print(first == second)
print(first is second)
Code Block #16
first = 5
second = 10 / 2
first == second
# True
first is second
# False
first
# 5
second
# 5.0
For more Python resources, you can also visit Real Python—you may even stumble on one of my own articles or courses there!
Also, are you interested in technical writing? You’d like to make your own writing more narrative, more engaging, more memorable? Have a look at Breaking the Rules.
And you can find out more about me at stephengruppetta.com