Q2 · Basics and data types
What is the difference between `is` and `==`?
== checks whether two objects have equal values. is checks whether two names point to the very same object in memory. Use == for comparing data and is only for singletons like None, True and False.
In simple words: == asks "do they look the same?"; is asks "are they the very same thing?".
Why it works
a == b calls the method a.__eq__(b). Methods with two underscores on each side, like __eq__, are called dunder methods (short for "double underscore"); Python calls them for you when you use operators. Each type decides what "equal" means. For lists, it compares items one by one. a is b does not call any method. It simply checks whether both names hold the same object identity. In CPython (the standard Python interpreter you download from python.org), identity is the memory address, and id(x) shows it as a number. That number changes every run, so this pack never prints it; we print the result of is instead.
Two lists with the same items are equal but are separate objects. Change one and the other does not change. That is why == is True and is is False below.
a = [1, 2, 3]
b = [1, 2, 3]
c = a
print(a == b, a is b) # equal values, different objects
print(a == c, a is c) # c is just another name for a
result = None
print(result is None) # the right way to check for NoneOutput, real run on Python 3.13.5:
True False
True True
TrueNow the famous trap. Sometimes is seems to work for numbers and strings. That is because of caching, which is a CPython implementation detail, not a language rule. CPython keeps one shared object for each small integer from -5 to 256, so any code that produces 100 gets the same object. Larger numbers made at runtime are usually separate objects. Short strings that look like identifiers (letters, digits, underscore) are often interned, which means the interpreter keeps a single copy and reuses it. You can ask for interning yourself with sys.intern().
import sys
x = int("100") # built at runtime from text
y = int("100")
print("100 is 100:", x is y) # small-int cache (CPython)
big1 = int("1000")
big2 = int("1000")
print("1000 is 1000:", big1 is big2, "| equal:", big1 == big2)
s1 = "".join(["py", "thon"]) # built at runtime
s2 = "python"
print("str is:", s1 is s2, "| equal:", s1 == s2)
print("after intern:", sys.intern(s1) is s2)Output, real run on Python 3.13.5:
100 is 100: True
1000 is 1000: False | equal: True
str is: False | equal: True
after intern: TrueWe used int("1000") on purpose. If you write a = 1000; b = 1000 in one file, the compiler may store one constant and both names point to it, so is can print True. That is exactly why you must never depend on is for numbers or strings: the answer depends on how the code was compiled and run, not on the values.
Common mistakes
- Writing if name is "admin":. Python 3.8 and later even warn about this with SyntaxWarning: "is" with a literal. Use ==.
- Saying "is compares memory and == compares values" and stopping there. Add that == can be customised with __eq__, while is cannot be changed.
- Presenting the -5 to 256 range as a Python rule. Always call it a CPython implementation detail.
Follow-ups
- Why is x is None preferred over x == None? There is only one None object, so is is exact and fast. A class could define __eq__ to return True when compared with None, which would fool ==.
- Can two objects have the same id()? Yes, if their lifetimes do not overlap. After one object is freed, its address can be reused by a new object.
- Can a == a ever be False? Yes, for float('nan'). NaN is not equal to anything, including itself.
