The Built-In id()
Function
by Christoph Schiessl on Python
In addition to equality, you can also compare objects in Python based on their identity. Object identities are represented by simple int
values that you can retrieve using the built-in id(object)
function. All Python objects, without exception, have a unique identity. The official documentation explains the id()
function as follows:
Return the "identity" of an object. This is an integer which is guaranteed to be unique and constant for this object during its lifetime. Two objects with non-overlapping lifetimes may have the same
id()
value.
So, all objects in existence at any given time have unique identities.
Python 3.12.2 (main, Feb 17 2024, 22:03:42) [GCC 13.2.1 20230801] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> x, y = object(), object()
>>> id(x), id(y) # Two distinct objects have distinct identities.
(131886824916736, 131886824916784)
>>> id(x), id(y) # Identities never change => they are constant.
(131886824916736, 131886824916784)
So far, so good. The documentation explicitly said that the uniqueness of identities does not hold for objects whose lifetimes do not overlap, and this behavior is testable. Observe:
Python 3.12.2 (main, Feb 17 2024, 22:03:42) [GCC 13.2.1 20230801] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> prev_ids: set[int] = set()
>>> while True:
... current_id: int = id(object())
... if current_id in prev_ids:
... print(f"Identity of the {len(prev_ids) + 1}th object was seen before!")
... break
... prev_ids.add(current_id)
...
Identity of the 2th object was seen before!
On my machine, the second object created had the same identity as the first one, which is no longer accessible (no references to it are in scope). This is implementation-specific, but if you use CPython, as I am, the first object has been garbage-collected. I can be sure of that because CPython object identities are nothing more than memory addresses. So, if the same memory address appears again for a different object, this must mean that the garbage collector has freed the object that previously occupied this memory address.
When I wrote this program, I didn't expect to get a repeated memory address for the second object, but it doesn't matter. The lifetime of the first object has ended, and therefore, the memory address it used to occupy is available for other objects. Second or not, the id()
uniqueness, as required by the specification, has not been violated!
is
and is not
Operators
Object identities come into play when using the is
operator. For instance, if you imagine two references, a
and b
, you can compare the identities of the objects behind these references with the expression a is b
. This expression is equivalent to id(a) == id(b)
. So, it evaluates to True
if a
and b
are the same object (they occupy the same memory address in CPython). Otherwise, it is evaluated to be False
.
The is not
operator is the negation of the is
operator. So, the expression a is not b
is equivalent to id(a) != id(b)
. Therefore, it evaluates to True
if a
and b
are distinct objects (they occupy distinct memory addresses in CPython). Otherwise, it is evaluated to be False
.
I hope you found this article helpful. Anyway, thank you very much for reading, and see you again next time!