This is not a safe assumption for any type of variable, except booleans.
In general this behavior is known as "interning" - simple immutable values get stored and cached, to save a little bit of memory. As elaborated upon in this answer, small integers and Strings are usually interned, but not always, and relying on this behavior will lead you to some hard-to-debug errors.
int
and str
, and specifically the empty tuple ()
, are the only types I've observed to exhibit this behavior, and even then, it's unpredictable when they'll stop.
For most other simple immutable types (int
, float
, complex
, tuple
, bool
, bytes
) you can compare literals to each other and the interpreter will tell you True
, after spitting out a warning, but assigning them to variables and repeating the check will produce False
:
>>> (1, 2) is (1, 2)
<stdin>:1: SyntaxWarning: "is" with a literal. Did you mean "=="?
True
>>> a = (1, 2)
>>> b = (1, 2)
>>> a is b
False
Mutable or complex types (range
, list
, dict
, set
, frozenset
, bytearray
) will never occupy the same memory as each other, even if identical, and so even doing an is
comparison with literals will return false:
>>> bytearray((1, 2)) is bytearray((1, 2))
False
It should be obvious that any two different types will never return true
in an is
comparison.
Booleans, and None
, are a special case. They are defined as global constants, and in fact every invocation of True
and False
explicitly points to the same two instances in memory. You can safely use is
for comparing any two booleans.
This is actually useful when you want to disambiguate between a value that could be True
, False
, or None
. Simply checking truthiness (if not condition:
) would group False
and None
together, so you might need to instead check if condition is False:
to explicitly exclude None
.