Python object identity
Identity is one of three characteristics (along with type and
value) assigned to a Python object when it is created. It is represented
by a read-only integer value identifier, guaranteed to be unique during the
object’s lifetime (outside of which it may be reused). That identifier is
returned by the built-in function id()
. “This value,” Wesley J. Chun
suggests, “is as close as you will get to a ‘memory address’ in Python.” (That
is how it is implemented in CPython.)
While id()
does not have much everyday use, it offers a handy and easy way to
understand how Python uses objects.
>>> a = 1
>>> id(a)
4327371344
The identity reported here is the identity of the object referred to by the
name a
. In this example, that is an object representing the integer value 1.
If we assign the object referred to by a
to a second name, b
, we have
created a second reference to the same object.
>>> a = 1
>>> id(a)
4339520080
>>> b = a
>>> id(b)
4339520080
Note that in the following example, id()
will report that same identity
when called on (1) the integer literal, as a represented value; (2) the first
reference to it; (3) subsequent separate references to it, of three different
kinds. Assuming a fresh environment:
>>> id(1)
4402483792
>>> a = 1
>>> id(a)
4402483792
>>> b = 1
>>> id(b)
4402483792
>>> c = b
>>> id(1) == id(a) == id(b) == id(c)
True
>>> for _ in range(1, 2):
id(_)
4402483792
The reason for this is — though note, this is an implementation-dependent
feature — that Python caches, or “interns,” integers in the range -5 to 256,
which are likely to be used many times in a typical program. (For an
explanation including a walk through the relevant CPython source code, see
“‘is’ operator behaves unexpectedly with integers.”) Because this feature may
be present, and also because it is implementation-dependent, you should never
use the is
operator to compare integers. The value returned by id()
is a
memory address in CPython, but id()
might be implemented differently in
Python implemented on other platforms.
Boolean values
Booleans also behave this way:
>>> a = True
>>> b = True
>>> id(True) == id(a) == id(b)
True
Floating-point values
Since Python does not cache floating-point values in this way, equivalent floating-point literals within one expression will refer to the same object, but assignment in discrete expressions will create different objects:
>>> id(1.1) == id(1.1)
True
>>> a = 1.1
>>> b = 1.1
>>> id(a) == id(b)
False
>>> id(1.1) == id(a)
False
>>> id(1.1) == id(b)
False
That is, as long as you’re using the Python interpreter and you’re using separate, thus separately parsed expressions. Run them together on a line, giving the bytecode compiler a chance to optimize by using the same object for equivalent literals, and you’ll get back a single object:
>>> a = 1.1; b = 1.1; id(a) == id(b)
True
Sequences and collections
Sequences and collections are yet another case. Equivalent literals within one expression will refer to the same object, but that’s the only case.
>>> id([1, 2, 3]) == id([1, 2, 3])
True
>>> a = [1, 2, 3]
>>> b = [1, 2, 3]
>>> id(a) == id(b)
False
>>> id([1, 2, 3]) == id(a)
False
>>> a = [1, 2, 3]; b = [1, 2, 3]; id(a) == id(b)
False
>>> id((1, 2, 3)) == id((1, 2, 3))
False
>>> a = (1, 2, 3)
>>> b = (1, 2, 3)
>>> id(a) == id(b)
False
>>> id((1, 2, 3)) == id(a)
False
>>> a = (1, 2, 3); b = (1, 2, 3); id(a) == id(b)
False
>>> id({'a': 1}) == id({'a': 1})
True
>>> a = {'a': 1}
>>> b = {'a': 1}
>>> id(a) == id(b)
False
>>> id({'a': 1}) == id(a)
False
>>> a = {'a': 1}; b = {'a': 1}; id(a) == id(b)
False
However, literals within the sequence or collection will refer to the same object:
>>> a = [1, 2, 3]
>>> b = [1, 2, 3]
>>> id(a[0]) == id(b[0])
True
>>> a = (1, 2, 3)
>>> b = (1, 2, 3)
>>> id(a[1]) == id(b[1])
True
>>> a = {'a': 1}
>>> b = {'a': 1}
>>> id(a['a']) == id(b['a'])
True
Strings
Finally, strings are yet another case. Python also interns valid names — that is, symbols with an initial alphabetic character or underscore, followed by additional characters, if any, that are alphanumeric or underscore:
>>> a = "foo"
>>> b = "foo"
>>> id("foo") == id(a) == id(b)
True
>>> a = "foo bar"
>>> b = "foo bar"
>>> id(a) == id(b)
False
Sources
Chun, Wesley J. Core Python Programming. 2nd ed, Prentice Hall, 2007. Section 4.1: “Python Objects.”
felix021. “What’s with the Integer Cache inside Python?” Stack Overflow, March 2, 2013.
Hewgill, Greg. “‘is’ operator behaves unexpectedly with integers.” Stack Overflow, November 20, 2008.
Lutz, Mark. Learning Python. Third ed., O’Reilly, 2008. Chapter 6, “The Dynamic Typing Interlude.”
Vernier, Yann. “The internals of Python string interning.” Guilload.com, June 28, 2014.
“PyObject* PyLong_FromLong(long v).” Python/C API Reference Manual: Integer Objects.