# Understanding The Difference Between is and == in Python: The £5 Note and a Trip to a Coffee Shop

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.

• Find out what’s the difference between is and == in Python
• 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.

### 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:

1. Are two drinks equal if they have the same name?
2. Are two drinks equal if they have the same price?
3. 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.

• Found out what’s the difference between is and == in Python
• 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!