21

I want a Python object that I can use as a stack. Is it better to use a deque or a list and does it make a difference whether I have a small number of elements or a large number of elements?

Reblochon Masque
  • 35,405
  • 10
  • 55
  • 80
Casebash
  • 114,675
  • 90
  • 247
  • 350

1 Answers1

33

Your mileage may vary according to your application and precise use case, but in the general case, a list is well suited for a stack:

append is O(1)

pop() is O(1) - as long as you do not pop from an arbitrary position; pop() only from the end.

Deques are also O(1) for these operations, but require importing a module from the standard library, require 'O(n)' for random access. An argument could be made to prefer using vanilla lists though, unless for specific applicatins.

Correction:

from this post by Raymond Hettinger, a principal author of the C code for both list and deque, it appears that deques may perform slightly better than lists: The pop() operation of deques seem to have the advantage.

In [1]: from collections import deque

In [2]: s = list(range(1000))      # range(1000) if python 2

In [3]: d = deque(s)

In [4]: s_append, s_pop = s.append, s.pop

In [5]: d_append, d_pop = d.append, d.pop

In [6]: %timeit s_pop(); s_append(None)
10000000 loops, best of 3: 115 ns per loop

In [7]: %timeit d_pop(); d_append(None)
10000000 loops, best of 3: 70.5 ns per loop

the real differences between deques and list in terms of performance are:

Deques have O(1) speed for appendleft() and popleft() while lists have O(n) performance for insert(0, value) and pop(0).
List append performance is hit and miss because it uses realloc() under the hood. As a result, it tends to have over-optimistic timings in simple code (because the realloc doesn't have to move data) and really slow timings in real code (because fragmentation forces realloc to move all the data). In contrast, deque append performance is consistent because it never reallocs and never moves data.

Reblochon Masque
  • 35,405
  • 10
  • 55
  • 80
  • 3
    If you’re going to contrast two solutions, why emphasize the part where they’re the same? They’re not “arguably” O(1) with a deque – they *are* O(1). And what’s the supposed overhead? Got any benchmarks to back that up? – Ry- Nov 26 '17 at 05:56
  • I stand corrected regarding performance @Ryan - I posted an erratum. – Reblochon Masque Nov 26 '17 at 06:17
  • 4
    Note that the quoted post was made by Python core developer, Raymond Hettinger, a principal author of the C code for both [`list`](https://github.com/python/cpython/blob/3.6/Objects/listobject.c) and [`deque`](https://github.com/python/cpython/blob/3.6/Modules/_collectionsmodule.c#L10), so he knows what he's talking about! – PM 2Ring Nov 26 '17 at 06:43
  • I get: `AttributeError: 'range' object has no attribute 'append'` – Eduardo Pignatelli Nov 26 '20 at 14:42
  • I have updated the answer @EduardoPignatelli, it was correct in python 2, when `range` returned a `list`. – Reblochon Masque Nov 26 '20 at 14:51