Qual è la sintassi idiomatica per anteporre a un breve elenco Python?
Di solito non vuoi anteporre ripetutamente a un elenco in Python.
Se è corto e non lo fai molto ... allora ok.
list.insert
Il list.insert
può essere usato in questo modo.
list.insert(0, x)
Ma questo è inefficiente, perché in Python, a list
è un array di puntatori e Python ora deve prendere ogni puntatore nell'elenco e spostarlo di uno in basso per inserire il puntatore al tuo oggetto nel primo slot, quindi è davvero solo efficiente per elenchi piuttosto brevi, come chiedete.
Ecco uno snippet dal sorgente CPython in cui questo è implementato - e come puoi vedere, iniziamo alla fine dell'array e spostiamo tutto in basso di uno per ogni inserimento:
for (i = n; --i >= where; )
items[i+1] = items[i];
Se si desidera un contenitore / elenco efficiente per anteporre elementi, si desidera un elenco collegato. Python ha una lista doppiamente collegata, che può inserire all'inizio e alla fine in fretta - si chiama deque
.
deque.appendleft
A collections.deque
ha molti dei metodi di un elenco. list.sort
è un'eccezione, rendendo deque
definitivamente non completamente sostituibile Liskov list
.
>>> set(dir(list)) - set(dir(deque))
{'sort'}
Il deque
ha anche un appendleft
metodo (nonché popleft
). Il deque
è un Deque e una lista doppiamente legata - non importa la lunghezza, ci vuole sempre la stessa quantità di tempo per preprend qualcosa. Nella notazione O grande, O (1) rispetto al tempo O (n) per gli elenchi. Ecco l'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
Altrettanto rilevante è il extendleft
metodo del deque , che antepone in modo iterativo:
>>> from collections import deque
>>> d2 = deque('def')
>>> d2.extendleft('cba')
>>> d2
deque(['a', 'b', 'c', 'd', 'e', 'f'])
Si noti che ogni elemento verrà anteposto uno alla volta, in modo da invertire effettivamente il loro ordine.
Performance di list
controdeque
Per prima cosa installiamo con alcune anteprime iterative:
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 prestazioni:
>>> 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
Il deque è molto più veloce. Man mano che le liste si allungano, mi aspetto che un deque si esibisca ancora meglio. Se puoi usare i deque extendleft
probabilmente otterrai le migliori prestazioni in quel modo.