is Operator (Identity Check)
Python's is operator checks object identity. While == compares whether values are equal, is checks whether two references point to the same object in memory. PEP 8 convention is to use is (not ==) when comparing against None, True, or False. On the other hand, integer and string interning (internal caching) can cause is to return True by coincidence, so using is for value comparison is dangerous.
Syntax
# Check whether two references point to the same object (returns True or False).
object_a is object_b
# Check whether two references point to different objects (returns True or False).
object_a is not object_b
# Recommended pattern for None checks
if variable is None:
# code when variable is None
if variable is not None:
# code when variable is not None
Operators
| Syntax | Notes |
|---|---|
| a is b | Returns True if a and b are the same object (same memory address). |
| a is not b | Returns True if a and b are different objects. |
| a == b | Returns True if the values of a and b are equal. Can be True even if they are not the same object. |
| variable is None | Checks whether a variable is None. PEP 8 recommends using is for None comparisons. |
| variable is not None | Checks whether a variable is not None. PEP 8 recommends using is not for None comparisons. |
Sample Code
Comparing is and ==. This sample uses characters from the Yakuza (Like a Dragon) series.
is_vs_equal.py
# Define character data as dictionaries.
kiryu = {"name": "Kiryu Kazuma", "title": "Dragon"}
majima = {"name": "Majima Goro", "title": "Mad Dog"}
# Create a separate object with the same content.
kiryu_copy = {"name": "Kiryu Kazuma", "title": "Dragon"}
# == compares values.
print(kiryu == kiryu_copy) # True (same content)
print(kiryu == majima) # False (different content)
# is checks identity (same memory address).
print(kiryu is kiryu_copy) # False (two separately created objects)
print(kiryu is majima) # False
# Assigning the same object to another variable makes is return True.
kiryu_ref = kiryu
print(kiryu_ref is kiryu) # True (points to the same object)
print(kiryu_ref == kiryu) # True
# id() shows the memory address.
print(id(kiryu)) # e.g. 140234567890
print(id(kiryu_ref)) # same address
print(id(kiryu_copy)) # different address
python3 is_vs_equal.py True False False False True True 140234567890128 140234567890128 140234567891264
Using is for None checks. Since None is a singleton (only one exists in Python), comparing with is is reliable.
none_check.py
# Manage character data in a dictionary. None means not set.
character_data = {
"kiryu": {"name": "Kiryu Kazuma", "partner": None},
"majima": {"name": "Majima Goro", "partner": "Nishida"},
"saejima": {"name": "Saejima Taiga", "partner": None},
}
# Use is None to check for unset values (recommended).
for key, data in character_data.items():
if data["partner"] is None:
print(data["name"] + ": no partner set")
else:
print(data["name"] + " partner: " + data["partner"])
print()
# == None also works, but is None is preferred.
# Reason: objects with __eq__ overridden may behave unexpectedly with == None.
value = None
print(value is None) # True (recommended)
print(value == None) # True (works but not preferred)
# Use is not None to handle the case where a value exists.
def get_title(name):
titles = {"Kiryu Kazuma": "Dragon", "Majima Goro": "Mad Dog"}
return titles.get(name) # Returns None if not found.
title = get_title("Kiryu Kazuma")
if title is not None:
print("Title: " + title)
else:
print("No title")
python3 none_check.py Kiryu Kazuma: no partner set Majima Goro partner: Nishida Saejima Taiga: no partner set True True Title: Dragon
The pitfall of integer and string interning with is. Small integers and short strings may cause is to return True by coincidence, but this cannot be relied upon. When running as a script, compile-time constant folding may cause is to return True even for integers above 256 or strings containing special characters. In interactive mode (REPL), False is the more common result, and the outcome varies depending on how the code is executed.
interning_pitfall.py
# Integer interning: Python caches integers from -5 to 256. # Within this range, integers with the same value are the same object. a = 100 b = 100 print(a is b) # True (cached in the -5 to 256 range) print(a == b) # True # Above 256, separate objects may be created (varies by environment). # When run as a script, CPython performs constant folding and merges # identical literals within the same code object, resulting in True. x = 1000 y = 1000 print(x is y) # True (constant folding at compile time merges the literals) print(x == y) # True (values are equal) # String interning: simple identifier-like strings are likely to be interned. name1 = "kiryu" name2 = "kiryu" print(name1 is name2) # True (often interned) # Strings with spaces can also be merged by constant folding when run as a script. title1 = "Kiryu Kazuma" title2 = "Kiryu Kazuma" print(title1 is title2) # True (constant folding at compile time merges the literals) print(title1 == title2) # True (value comparison is reliable) # Summary: use == for string and integer value comparisons. # Results with is can vary across environments and Python versions.
python3 interning_pitfall.py True True True True True True True
Why isinstance() is used for type checking. is and type() == do not account for inheritance.
type_check.py
# Define a base class and a derived class.
class Character:
def __init__(self, name):
self.name = name
class MainCharacter(Character):
def __init__(self, name, title):
super().__init__(name)
self.title = title
# Create instances.
kiryu = MainCharacter("Kiryu Kazuma", "Dragon")
npc = Character("Passerby A")
# isinstance() includes subclasses in the check (recommended).
print(isinstance(kiryu, MainCharacter)) # True
print(isinstance(kiryu, Character)) # True (MainCharacter is a subclass of Character)
print(isinstance(npc, MainCharacter)) # False
# type() == does not consider subclasses.
print(type(kiryu) == MainCharacter) # True
print(type(kiryu) == Character) # False (subclass not included)
# type() is also does not consider inheritance.
print(type(kiryu) is Character) # False
# Use isinstance() for code that leverages polymorphism.
characters = [kiryu, npc, MainCharacter("Majima Goro", "Mad Dog")]
for chara in characters:
if isinstance(chara, MainCharacter):
print(chara.name + " (" + chara.title + ") is a main character")
else:
print(chara.name + " is a generic character")
python3 type_check.py True True False True False False Kiryu Kazuma (Dragon) is a main character Passerby A is a generic character Majima Goro (Mad Dog) is a main character
Common Mistakes
Common Mistake 1: Using is for value comparison and depending on interning
Small integers (-5 to 256) and short identifier-like strings are cached (interned) by Python. Within this range, is happens to return True, but outside the range or on a different execution environment the result can change. Writing literals directly in a script can cause constant folding to return True, so the sample below uses variable-based computation to reliably produce separate objects. Use == for value comparisons.
ng_intern_trap.py
a = 256 b = 256 print(a is b) # True (within the interned range, coincidentally True) base = 250 x = base + 7 # 257 y = base + 7 # 257 (separate computation, separate object) print(x is y) # False (separate objects) name1 = "kiryu" name2 = "kiryu" print(name1 is name2) # True (likely to be interned) prefix = "Kiryu" title1 = prefix + " Kazuma" title2 = prefix + " Kazuma" # separate concatenation, separate object print(title1 is title2) # False (separate objects)
python3 ng_intern_trap.py True False True False
ok_equal_compare.py
# Use == for value comparisons. a = 257 b = 257 print(a == b) # True (reliable regardless of environment) name1 = "Kiryu Kazuma" name2 = "Kiryu Kazuma" print(name1 == name2) # True (value equality comparison)
python3 ok_equal_compare.py True True
Common Mistake 2: Using == None where is None should be used
Using == None on an object from a custom class with __eq__ overridden may produce unexpected results. Using is None checks identity without depending on the __eq__ implementation, making it reliable.
ng_eq_none.py
# Example of a class with __eq__ overridden
class Dominator:
def __eq__(self, other):
# Wrong: __eq__ always returns True (extreme example)
return True
d = Dominator()
print(d == None) # True (__eq__ override causes unexpected behavior)
print(d is None) # False (identity check is accurate)
python3 ng_eq_none.py True False
ok_is_none.py
character_data = {
"kiryu": {"name": "Kiryu Kazuma", "partner": None},
"majima": {"name": "Majima Goro", "partner": "Nishida"},
}
for key, data in character_data.items():
# is None is not affected by __eq__.
if data["partner"] is None:
print(data["name"] + ": no partner set")
else:
print(data["name"] + ": " + data["partner"])
python3 ok_is_none.py Kiryu Kazuma: no partner set Majima Goro: Nishida
Common Mistake 3: Confusing is not with not is, causing SyntaxError
is not None is a two-word operator. Writing not is None causes a SyntaxError. Similarly, not in is a two-word operator; in not is incorrect.
ng_not_is.py
value = "Kiryu Kazuma"
# This works syntactically but is not recommended by PEP 8.
if not value is None:
print("value exists")
ok_is_not.py
value = "Kiryu Kazuma"
# Use is not (two-word operator).
if value is not None:
print("value exists:", value)
result = None
if result is None:
print("result is not set")
python3 ok_is_not.py value exists: Kiryu Kazuma result is not set
Notes
The distinction between is and == matters. is checks object identity (same memory address), while == checks value equality. Using is for value comparison is an error; even if interning causes it to return True for integers and strings, that is an implementation detail and is not guaranteed to hold in the future.
PEP 8 convention is to use is (and is not) when comparing against None. None is a singleton (only one exists in Python), so comparing with is None is reliable and clearly expresses intent. While == None works in most cases, it may behave unexpectedly when compared with objects whose __eq__ is overridden.
For type checking, isinstance() is common. Using type(obj) is SomeClass or type(obj) == SomeClass cannot account for subclasses, so care is needed. isinstance() includes subclasses in its check, making it suitable for code that leverages polymorphism. See isinstance() / issubclass() / type() for details. For the distinction from the in operator (containment check), see in operator.
If you find any errors or copyright issues, please contact us.