Какой идиоматический синтаксис для добавления в короткий список Python?
Обычно вы не хотите повторяться перед списком в Python.
Если это коротко , и вы не делаете это много ... тогда хорошо.
list.insert
list.insert
Можно использовать таким образом.
list.insert(0, x)
Но это неэффективно, потому что в Python a list
является массивом указателей, и Python должен теперь взять каждый указатель в списке и переместить его вниз на единицу, чтобы вставить указатель на ваш объект в первом слоте, так что это действительно только эффективно для довольно коротких списков, как вы просите.
Вот фрагмент из источника CPython, где это реализовано - и, как вы можете видеть, мы начинаем с конца массива и перемещаем все вниз на единицу для каждой вставки:
for (i = n; --i >= where; )
items[i+1] = items[i];
Если вам нужен контейнер / список, эффективный для добавления элементов, вам нужен связанный список. Python имеет двусвязный список, который можно быстро вставить в начало и в конец - он называется a deque
.
deque.appendleft
А collections.deque
имеет много методов списка. list.sort
исключение, делающее deque
окончательно не полностью заменяемым Лискова list
.
>>> set(dir(list)) - set(dir(deque))
{'sort'}
deque
Также имеет appendleft
метод (а также popleft
). Это deque
двусторонняя очередь и двусвязный список - независимо от длины, всегда требуется одинаковое количество времени для предварительной подготовки чего-либо. В больших обозначениях O время O (1) и время O (n) для списков. Вот использование:
>>> import collections
>>> d = collections.deque('1234')
>>> d
deque(['1', '2', '3', '4'])
>>> d.appendleft('0')
>>> d
deque(['0', '1', '2', '3', '4'])
deque.extendleft
Также актуален метод deque extendleft
, который итеративно добавляет:
>>> from collections import deque
>>> d2 = deque('def')
>>> d2.extendleft('cba')
>>> d2
deque(['a', 'b', 'c', 'd', 'e', 'f'])
Обратите внимание, что каждый элемент будет добавляться по одному за раз, таким образом, эффективно изменяя их порядок.
Производительность list
противdeque
Сначала мы настроим итеративное добавление:
import timeit
from collections import deque
def list_insert_0():
l = []
for i in range(20):
l.insert(0, i)
def list_slice_insert():
l = []
for i in range(20):
l[:0] = [i] # semantically same as list.insert(0, i)
def list_add():
l = []
for i in range(20):
l = [i] + l # caveat: new list each time
def deque_appendleft():
d = deque()
for i in range(20):
d.appendleft(i) # semantically same as list.insert(0, i)
def deque_extendleft():
d = deque()
d.extendleft(range(20)) # semantically same as deque_appendleft above
и производительность:
>>> min(timeit.repeat(list_insert_0))
2.8267281929729506
>>> min(timeit.repeat(list_slice_insert))
2.5210217320127413
>>> min(timeit.repeat(list_add))
2.0641671380144544
>>> min(timeit.repeat(deque_appendleft))
1.5863927800091915
>>> min(timeit.repeat(deque_extendleft))
0.5352169770048931
Deque намного быстрее. Поскольку списки становятся длиннее, я ожидаю, что deque будет работать еще лучше. Если вы можете использовать deque, extendleft
вы, вероятно, получите лучшую производительность таким образом.