Как список элементов списка выглядит O (1) в Python?

Сегодня в классе мы узнали, что извлечение элемента из списка - это O(1) в Python. Почему это так? Предположим, у меня есть список из четырех элементов, например:

li = ["perry", 1, 23.5, "s"]

Эти элементы имеют разные размеры в памяти. И поэтому невозможно взять ячейку памяти li[0] и добавить в три раза больше размера каждого элемента, чтобы получить ячейку памяти li[3]. Итак, как интерпретатор знает, где li[3] не нуждается в перемещении списка для извлечения элемента?

Ответ 1

Список в Python реализуется как массив указателей 1. Итак, что действительно происходит при создании списка:

["perry", 1, 23.5, "s"]

что вы на самом деле создаете массив указателей, например:

[0xa3d25342, 0x635423fa, 0xff243546, 0x2545fade]

Каждый указатель "указывает" на соответствующие объекты в памяти, так что строка "perry" будет сохранена по адресу 0xa3d25342 а номер 1 будет сохранен в 0x635423fa и т.д.

Поскольку все указатели имеют одинаковый размер, интерпретатор может фактически добавить в 3 раза размер элемента к адресу li[0] чтобы перейти к указателю, хранящемуся в li[3].


1 Получить более подробную информацию: конский рот (исходный код CPython на GitHub).

Ответ 2

Когда вы говорите a = [...], a фактически является указателем на PyObject содержащий массив указателей на PyObject s.

Когда вы запрашиваете a[2], интерпретатор сначала следует указателю на список PyObject, затем добавляет 2 к адресу массива внутри него, а затем возвращает этот указатель. То же самое происходит, если вы попросите a[0] или a[9999].

В принципе, все объекты Python доступны по ссылке вместо значения, даже целые литералы, такие как 2. В системе указателей есть несколько трюков, чтобы все это было эффективным. И указатели имеют известный размер, поэтому их удобно хранить в массивах C-стиля.

Ответ 3

Короткий ответ: списки Python - это массивы.

Длинный ответ. Список терминов компьютерной науки обычно означает либо связанный список (как используется в функциональном программировании), либо список с двойной связью (как используется в процедурном программировании). Эти структуры данных поддерживают ввод O (1) либо в начале списка (функционально), либо в любом месте, которое не нужно искать (процедурно). Список "Python" не имеет ни одной из этих характеристик. Вместо этого он поддерживает (амортизируется) O (1), добавляемый в конце списка (например, C++ std :: vector или Java ArrayList). Списки Python - это действительно масштабируемые массивы в терминах CS.

Следующий комментарий из документации Python объясняет некоторые характеристики производительности списков Python:

Также можно использовать список в качестве очереди, где первым добавленным элементом является первый извлеченный элемент ("first-in, first-out"); однако списки для этой цели неэффективны. В то время как appends и pops из конца списка бывают быстрыми, выполнение вставок или всплывающих окон с начала списка происходит медленно (потому что все остальные элементы должны быть сдвинуты на один).