Qual é a sintaxe idiomática para anexar uma pequena lista python?
Geralmente, você não deseja anexar repetidamente uma lista em Python.
Se for curto e você não estiver fazendo muito ... então tudo bem.
list.insert
O list.insert
pode ser usado dessa maneira.
list.insert(0, x)
Mas isso é ineficiente, porque, em Python, a list
é uma matriz de ponteiros, e agora o Python deve pegar todos os ponteiros da lista e movê-lo para baixo em um para inserir o ponteiro no seu objeto no primeiro slot, portanto, isso é realmente apenas eficiente para listas bastante curtas, como você pede.
Aqui está um trecho da fonte CPython onde isso é implementado - e como você pode ver, começamos no final da matriz e movemos tudo para baixo em um para cada inserção:
for (i = n; --i >= where; )
items[i+1] = items[i];
Se você deseja um contêiner / lista eficiente na adição de elementos, deseja uma lista vinculada. O Python tem uma lista duplamente vinculada, que pode ser inserida no início e no final rapidamente - é chamada de a deque
.
deque.appendleft
A collections.deque
tem muitos dos métodos de uma lista. list.sort
é uma exceção, tornando deque
definitivamente não inteiramente Liskov substituível list
.
>>> set(dir(list)) - set(dir(deque))
{'sort'}
O deque
também possui um appendleft
método (assim como popleft
). O deque
é um deque e uma lista duplamente vinculada - não importa o comprimento, ele sempre leva a mesma quantidade de tempo para preprend algo. Na notação O grande, O (1) versus o tempo O (n) das listas. Aqui está o uso:
>>> import collections
>>> d = collections.deque('1234')
>>> d
deque(['1', '2', '3', '4'])
>>> d.appendleft('0')
>>> d
deque(['0', '1', '2', '3', '4'])
deque.extendleft
Também relevante é o extendleft
método de deque , que precede iterativamente:
>>> from collections import deque
>>> d2 = deque('def')
>>> d2.extendleft('cba')
>>> d2
deque(['a', 'b', 'c', 'd', 'e', 'f'])
Observe que cada elemento será anexado um de cada vez, revertendo efetivamente sua ordem.
Desempenho de list
versusdeque
Primeiro, configuramos com alguns anexos iterativos:
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
e desempenho:
>>> 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
O deque é muito mais rápido. À medida que as listas ficam mais longas, eu esperaria que um deque tivesse um desempenho ainda melhor. Se você pode usar o deque, extendleft
provavelmente obterá o melhor desempenho dessa maneira.