Ответ 1
Нет, это не дешево. Удаление элемента с передней части списка (например, с помощью list.pop(0)
) - это операция O(N)
, и ее следует избегать. Точно так же вставка элементов в начале (с использованием list.insert(0, <value>)
) одинаково неэффективна.
Это происходит потому, что после изменения размера списка его элементы должны быть сдвинуты. Для CPython в случае l.pop(0)
это выполняется с помощью memmove
, а для l.insert(0, <value>)
, сдвиг выполняется с помощью цикла через сохраненные элементы.
Списки создаются для быстрого оперативного доступа и O(1)
операций на их конце.
Поскольку вы обычно выполняете эту операцию, вам следует рассмотреть возможность использования deque
из модуля collections
(as @ayhan предложил в комментарии). Документы на deque
также показывают, как объекты list
не подходят для этих операций:
Хотя объекты списка поддерживают подобные операции, они оптимизированы для операций с быстрой фиксированной длиной и несут затраты на перемещение памяти
O(N)
для операцийpop(0)
иinsert(0, v)
, которые изменяют размер и положение базового представления данных.
(Акцент мой)
Структура данных deque
предлагает O(1)
сложность для обеих сторон (начало и конец) с помощью методов appendleft
/popleft
и append
/pop
для начала и конца соответственно.
Конечно, с небольшими размерами это требует некоторых дополнительных требований к пространству (из-за структуры deque
), которая обычно не должна беспокоить (и, как отмечает @juanpa в комментарии, не всегда выполняется), поскольку размеры списков растут. Наконец, как замечательные примечания к комментариям @ShadowRanger, с очень маленькими размерами последовательностей проблема сбрасывания или вставки с фронта тривиально до такой степени, что на самом деле это не имеет никакого отношения.
Итак, вкратце, для списков со многими элементами используйте deque
, если вам нужны быстрые добавления/всплывающие окна с обеих сторон, иначе, если вы случайно получаете доступ и добавляете в конец, используйте list
s.