The keyword is
and the operator ==
are not the same. Yet, many programmers often use one when they should use the other. Let’s look at the difference between is
and ==
in Python with the help of a £5 note and a trip to my local coffee shop.
In this article, you’ll:
- Find out what’s the difference between
is
and==
in Python - Read about the identity of a Python object
- Explore how to control the behaviour of
==
in classes you define - Learn when you should use
is
and when==
in your code
Let me tell you about when I met my friend for a coffee the other day and the £5 note I had in my pocket.
A Trip to Meet a Friend in a Coffee Shop
I had agreed to meet a friend in a coffee shop the other day. Before I left, I put a £5 note in my pocket. It looked like this:

I met my friend James on the way to the coffee shop. We went in and ordered our two coffees. The total for the two coffees was £5. I took the £5 note out of my pocket, but James had also taken out a £5 from his pocket.
It’s that awkward “I’ll pay, you’ll pay” moment.
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 so she can move on and serve the next customer. The queue was getting quite long now.
From the barista’s point of view, all that matters is that (payment == 5)
will be True
.
So I let James pay!
The barista cares about the value of the note and not whether it’s the one that came from my pocket or James’s. Both notes have the same value.
Picking up the Note From the Floor
I mentioned there was a queue building up by the time James and I got our coffees and paid. But I didn’t tell you why.
As we were queueing to order, we spotted a £5 note on the floor next to our feet. We both checked our pockets, and we were 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 above. 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 searched 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. The pseudo-Python for this is that (note_on_the_floor is stephens_note)
will be True
.
Let’s learn more about the difference between is
and ==
in Python.
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):
>>> stephens_coffees = ["Double espresso", "Macchiato", "Espresso"] >>> jamess_coffees = ["Double espresso", "Macchiato"]
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:
>>> jamess_coffees.append("Espresso") >>> stephens_coffees ['Double espresso', 'Macchiato', 'Espresso'] >>> jamess_coffees ['Double espresso', 'Macchiato', 'Espresso']
How about now? Are the two lists the same?
You may have noticed I’ve italicised “the same” each time I’ve 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 have drunk the same kinds of coffee today? Well, if that’s the case, then yes, the values within the lists are the same and in the same order:
>>> stephens_coffees == jamess_coffees True
The lists are equal, and you used the equality operator ==
to check for this.
But the two lists are 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()
:
>>> id(stephens_coffees) 4689761920 >>> id(jamess_coffees) 4689190848
The two lists have different identities as shown by their “serial number”, 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:
>>> stephens_coffees is jamess_coffees False
The identity of the lists is not the same, and you used the is
keyword to check for this.
I also keep track of everything I eat and drink during the day:
>>> stephens_food_and_drink = [stephens_coffees, ["Sandwich", "Pasta"]] >>> stephens_food_and_drink [['Double espresso', 'Macchiato', 'Espresso'], ['Sandwich', 'Pasta']]
You place the list stephens_coffee
in the new list stephens_food_and_drink
and add a second item with food.
What will the output of this code be?
>>> stephens_food_and_drink[0] is stephens_coffees
Is the first element of stephens_food_and_drink
the same object as stephens_coffees
? We know it will be equal as it has the same items in the same order inside. Let’s find out if it’s also the exact same list:
>>> stephens_food_and_drink[0] is stephens_coffees True
Yes, it is. When you place a list within another list, you’re not creating a copy of the list, but you’re placing a reference to the same list.
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:
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)
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. 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:
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)
The objects espresso
and another_espresso
have the same values for .name
and .price
. However, both the equality and 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 define 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__()
defined, 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 ==
will 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:
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)
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 which charges £4 for the coffee.
Note that you’re no longer checking whether the identity of these objects is the same using is
. They’re all different objects.
The output from the three equality checks are:
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 names.
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:
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)
The only change is in the __eq__()
dunder method’s return
statement in the example above. 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, the rip_off_espresso
is no longer equal to espresso
, as 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
:
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)
On this occasion, only espresso
and another_espresso
are equal as they’re the only ones which 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 and to 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):
>>> 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
You get the expected result when you check for equality using the ==
operator. However, the two examples return a different result 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 print()
functions to show the results:
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)
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:
>>> first = 5 >>> second = 10 / 2 >>> first == second True >>> first is second False >>> first 5 >>> second 5.0
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 an equal value.
You rarely need to use is
in many applications. 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
. However, this is merely a convention, and you can use ==
to check whether an object is equal to None
.
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.
In this article, you’ve:
- Found out what’s the difference between
is
and==
in Python - Read about the identity of a Python object
- Explored how to control the behaviour of
==
in classes you define - Learned when you should use
is
and when==
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!
Further Reading
- The equality operator
==
is introduced in Chapter 1 in The Python Coding Book - You can read more about classes and dunder methods in Chapter 7 | Object-Oriented Programming
Subscribe to
The Python Coding Stack
Regular articles for the intermediate Python programmer or a beginner who wants to “read ahead”