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
== 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
- Read about the identity of a Python object
- Explore how to control the behaviour of
==in classes you define
- Learn when you should use
==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
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
Let’s learn more about the difference between
== in Python.
The Difference Between
== 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(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
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 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 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:
Next, you create two instances of this class. You check for equality using
== and whether the objects are the same object using the keyword
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)
another_espresso have the same values for
.price. However, both the equality and identity checks return
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,
== will return the same value.
__eq__() and explore the three options listed above.
1. Drinks are equal if they have the same name
__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’
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,
another_espresso are equal, but so are
rip_off_espresso, as they all have the same name,
"Espresso". It doesn’t matter that one is more expensive than the other two.
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,
macchiato, are all equal now. Even the macchiato is equal to the two espressos as it costs the same amount.
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
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
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
Here’s another example showing when checking for equality should be used instead of the
>>> 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
You’re now familiar with the difference between
== 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
- Read about the identity of a Python object
- Explored how to control the behaviour of
==in classes you define
- Learned when you should use
==in your code
And if you can’t recall the difference between
==, get your wallet and look at any banknote in there to remind you about my little adventure with James in our local coffee shop!
- 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
The Python Coding Stack
Regular articles for the intermediate Python programmer or a beginner who wants to “read ahead”