print(id([]) == id([]))
# prints 'True'
print(id(list()) == id(list()))
# prints 'False'
x = []
y = []
print(id(x) == id(y))
# prints 'False'
Why does list()
behave differently from []
, in regards to the above code?
print(id([]) == id([]))
# prints 'True'
print(id(list()) == id(list()))
# prints 'False'
x = []
y = []
print(id(x) == id(y))
# prints 'False'
Why does list()
behave differently from []
, in regards to the above code?
In your third id
comparison, you are comparing the ID values of two objects with overlapping lifetimes. This must return False, by the contract of the id
function. You would see the same behavior with list()
.
In both of your first two id
comparisons, the objects involved have nonoverlapping lifetimes. Whether the ID values of objects with nonoverlapping lifetimes happen to be the same is an implementation detail, and you should not rely on it being one way or the other. The behavior is subject to change without notice.
In current CPython, the ID values happen to be the same with []
because []
uses the BUILD_LIST
opcode, which invokes the C function PyList_New
, and PyList_New
uses a free list of deallocated list header structs to speed up allocation:
PyObject *
PyList_New(Py_ssize_t size)
{
...
if (numfree) {
numfree--;
op = free_list[numfree];
_Py_NewReference((PyObject *)op);
When lists are deallocated, the buffer holding the element pointers gets freed, but (up to a maximum free list size) the header holding information about stuff like object type, refcount, capacity, etc. goes on the free list:
static void
list_dealloc(PyListObject *op)
{
...
if (numfree < PyList_MAXFREELIST && PyList_CheckExact(op))
free_list[numfree++] = op;
else
Py_TYPE(op)->tp_free((PyObject *)op);
Py_TRASHCAN_SAFE_END(op)
}
The list created by the first []
dies before the second []
expression, so its header goes on the free list, and then it gets reused by the second []
. The id
value is based on this header's address, so both lists have the same ID value.
In contrast, list()
goes through tp_new
and tp_init
, and list
's tp_new
is PyType_GenericNew
, which doesn't go through the same free list. PyType_GenericNew
happens to allocate the two lists in different memory, producing different ID values.
id(object)
returns the id of the object.
An ==
expression evaluates to True if the objects referred to by the variables are equal (have the same contents).
So when you use the list()
constructor each time a new object is created, and it's id is different, hence it evaluates to false, even though a new object is immediately discarded, it is created at first, thus the different id.
To the contrary, the []
is literal (a faster way to create an object) and always has the same ID, but as it creates a new object, the new object also gets it's new ID.
TLDR; []
is a literal and thus has a fixed ID, list()
creates a new object, x=[]
y=[]
creates a new object x and y, hence the id of x
and y
are not the same, list()
creates a new object each time, thus different id on each call.
Also x = []
will be faster than x = list()
, but that is just a foot note and I can't figure out how to make it small, and put it like a foot note :)