При третьем сравнении id
вы сравниваете значения идентификаторов двух объектов с перекрывающимися временами жизни.Это должно вернуть False по контракту функции id
.Вы бы видели то же поведение с list()
.
. В обоих ваших первых двух id
сравнениях соответствующие объекты имеют непересекающиеся времена жизни.Являются ли значения идентификаторов объектов с непересекающимися временами жизни одинаковыми, это деталь реализации, и вы не должны полагаться на то, что они так или иначе.Поведение может быть изменено без уведомления.
В текущем CPython значения идентификаторов совпадают с []
, потому что []
использует код операции BUILD_LIST
, который вызываетфункция C PyList_New
и PyList_New
использует свободный список освобожденных структур заголовка списка для ускорения выделения:
PyObject *
PyList_New(Py_ssize_t size)
{
...
if (numfree) {
numfree--;
op = free_list[numfree];
_Py_NewReference((PyObject *)op);
Когда списки освобождаются, буфер, содержащий элементуказатели освобождаются, но (до максимального размера свободного списка) заголовок, содержащий информацию о таких вещах, как тип объекта, счет, емкость и т. д. попадает в свободный список :
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)
}
Список, созданный первым []
, умирает перед вторым []
выражением, поэтому его заголовок попадает в свободный список, а затем снова используется вторым []
.Значение id
основано на адресе этого заголовка, поэтому оба списка имеют одинаковое значение идентификатора.
Напротив, list()
проходит через tp_new
, tp_init
и list
's *.1036 * - это PyType_GenericNew
, который не проходит через тот же список бесплатных.PyType_GenericNew
происходит выделение двух списков в разной памяти, создавая разные значения идентификаторов.