In Python how do I write code which shifts off the last element of a list and adds a new one to the beginning - to run as fast as possible at execution?
There are good solutions involving the use of append, rotate etc but not all may translate to fast execution.
A list can do fast inserts and removals of items only at its end. You'd use pop(-1)
and append
, and you'd end up with a stack.
Instead, use collections.deque
, which is designed for efficient addition and removal at both ends. Working on the "front" of a deque uses the popleft
and appendleft
methods. Note, "deque" means "double ended queue", and is pronounced "deck".
L = [1, 2, 3]
L.pop() # returns 3, L is now [1, 2]
L.append(4) # returns None, L is now [1, 2, 4]
L.insert(0, 5) # returns None, L is now [5, 1, 2, 4]
L.remove(2) # return None, L is now [5, 1, 4]
del(L[0]) # return None, L is now [1, 4]
L.pop(0) # return 1, L is now [4]
I ran some benchmarks for you. Here are the results.
TL;DR You probably want to use a deque
. Otherwise, insert
/ append
, or pop
/ del
are fine.
from collections import deque
import perfplot
# Add to end
def use_append(n):
"adds to end"
a = [1,2,3,4,5,6,7,8,9,10]*n
a.append(7)
return 1
def use_insert_end(n):
"adds to end"
a = [1,2,3,4,5,6,7,8,9,10]*n
a.insert(len(a),7)
return 1
def use_add_end(n):
"adds to end"
a = [1,2,3,4,5,6,7,8,9,10]*n
a = a + [7]
return 1
perfplot.show(
setup=lambda n: n, # or simply setup=numpy.random.rand
kernels=[
lambda a: use_append(a),
lambda a: use_insert_end(a),
lambda a: use_add_end(a),
],
labels=["use_append", "use_insert_end", "use_add_end"],
n_range=[2 ** k for k in range(15)],
xlabel="len(a)",
)
# Removing from the end
def use_pop(n):
"removes from end"
a = [1,2,3,4,5,6,7,8,9,10]*n
a.pop()
return 1
def use_del_last(n):
"removes from end"
a = [1,2,3,4,5,6,7,8,9,10]*n
del(a[-1])
return 1
def use_index_to_end(n):
"removes from end"
a = [1,2,3,4,5,6,7,8,9,10]*n
a = a[:-1]
return 1
perfplot.show(
setup=lambda n: n,
kernels=[
lambda a: use_pop(a),
lambda a: use_del_last(a),
lambda a: use_index_to_end(a),
],
labels=["use_pop", "use_del_last", "use_index_to_end"],
n_range=[2 ** k for k in range(20)],
xlabel="len(a)",
)
# Add to beginning
def use_insert(n):
"adds to beginning"
a = [1,2,3,4,5,6,7,8,9,10]*n
a.insert(0,7)
return 1
def use_deque_appendleft(n):
"adds to beginning"
a = [1,2,3,4,5,6,7,8,9,10]*n
a = deque(a)
a.appendleft(7)
return 1
def use_add_start(n):
"adds to beginning"
a = [1,2,3,4,5,6,7,8,9,10]*n
a = [7] + a
return 1
perfplot.show(
setup=lambda n: n, # or simply setup=numpy.random.rand
kernels=[
lambda a: use_insert(a),
lambda a: use_deque_appendleft(a),
lambda a: use_add_start(a),
],
labels=["use_insert", "use_deque_appendleft","use_add_start"],
n_range=[2 ** k for k in range(15)],
xlabel="len(a)",
)
# Remove from beginning
def use_del_first(n):
"removes from beginning"
a = [1,2,3,4,5,6,7,8,9,10]*n
del(a[0])
return 1
def use_deque_popleft(n):
"removes from beginning"
a = [1,2,3,4,5,6,7,8,9,10]*n
a = deque(a)
a.popleft()
return 1
def use_index_start(n):
"removes from beginning"
a = [1,2,3,4,5,6,7,8,9,10]*n
a = a[1:]
return 1
perfplot.show(
setup=lambda n: n, # or simply setup=numpy.random.rand
kernels=[
lambda a: use_del_first(a),
lambda a: use_deque_popleft(a),
lambda a: use_index_start(a),
],
labels=["use_del_first", "use_deque_popleft", "use_index_start"],
n_range=[2 ** k for k in range(15)],
xlabel="len(a)",
)
Take these results with a grain of salt. Given how perfplot
works, the remove methods would get run multiple times, while setup is only run once. Hence, the list (or deque) needs to be generated locally in each function, which adds to run time.
I've modified the add methods below, and run a separate comparison for deque, to compare the effect of generating lists locally within the functions.
def gen_deque(n):
a = [1,2,3,4,5,6,7,8,9,10]*n if n > 0 else [1,2,3]
return deque(a)
def use_deque_appendleft(a):
"adds to beginning"
a.appendleft(7)
return 1
def use_deque_appendleft_original(a):
"adds to beginning"
a = [1,2,3,4,5,6,7,8,9,10]*(len(a)//10)
a = deque(a)
a.appendleft(7)
return 1
perfplot.show(
setup=lambda n: gen_deque(n), # or simply setup=numpy.random.rand
kernels=[
lambda a: use_deque_appendleft(a),
lambda a: use_deque_appendleft_original(a),
],
labels=["use_deque_appendleft", "use_deque_appendleft_original"],
n_range=[2 ** k for k in range(15)],
xlabel="len(a)",
)
# Add to end
def gen_data(n):
return [1,2,3,4,5,6,7,8,9,10]*n if n > 0 else [1,2,3]
def use_append(a):
"adds to end"
# a = [1,2,3,4,5,6,7,8,9,10]*n
a.append(7)
return 1
def use_insert_end(a):
"adds to end"
# a = [1,2,3,4,5,6,7,8,9,10]*n
a.insert(len(a),7)
return 1
def use_add_end(a):
"adds to end"
# a = [1,2,3,4,5,6,7,8,9,10]*n
a = a + [7]
return 1
perfplot.show(
setup=lambda n: gen_data(n), # or simply setup=numpy.random.rand
kernels=[
lambda a: use_append(a),
lambda a: use_insert_end(a),
lambda a: use_add_end(a),
],
labels=["use_append", "use_insert_end", "use_add_end"],
n_range=[2 ** k for k in range(15)],
xlabel="len(a)",
)
# Add to beginning
def gen_data(n):
return [1,2,3,4,5,6,7,8,9,10]*n if n > 0 else [1,2,3]
def use_insert(a):
"adds to beginning"
a.insert(0,7)
return 1
def use_deque_appendleft(a):
"adds to beginning"
a = deque(a)
a.appendleft(7)
return 1
def use_add_start(a):
"adds to beginning"
a = [7] + a
return 1
perfplot.show(
setup=lambda n: gen_data(n), # or simply setup=numpy.random.rand
kernels=[
lambda a: use_insert(a),
lambda a: use_deque_appendleft(a),
lambda a: use_add_start(a),
],
labels=["use_insert", "use_deque_appendleft","use_add_start"],
n_range=[2 ** k for k in range(5)],
xlabel="len(a)",
)
Insert and append have similar performance, and using a deque seems to have better performance than insert. As for del
/ pop
/ deque's popleft
, it seems that del
and pop
have similar performance, but it's hard to tell if deque's popleft
would be better, considering the overhead of generating lists / deques within each function for using perfplot
.
You can use insert method of python list object.
l = [1, 2, 3]
#lets insert 10 at biggining, which means at index 0
l.insert(0, 10)
print(l) # this will print [10, 1, 2, 3]
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.