https://github.com/python/cpython/blob/v3.8.1/Modules/_collectionsmodule.c
A
dequeobjectis composed of a doubly-linked list ofblocknodes.
So yes, a deque is a (doubly-)linked list as another answer suggests.
Elaborating: What this means is that Python lists are much better for random-access and fixed-length operations, including slicing, while deques are much more useful for pushing and popping things off the ends, with indexing (but not slicing, interestingly) being possible but slower than with lists.
Answer from JAB on Stack OverflowHow are deques in Python implemented, and when are they worse than lists? - Stack Overflow
queue - How Does Deque Work in Python - Stack Overflow
Python's deque is the stack object you want to use! I've written some tests to show the performance characteristics.
Double ended queue (deque). What is it used for?
Videos
https://github.com/python/cpython/blob/v3.8.1/Modules/_collectionsmodule.c
A
dequeobjectis composed of a doubly-linked list ofblocknodes.
So yes, a deque is a (doubly-)linked list as another answer suggests.
Elaborating: What this means is that Python lists are much better for random-access and fixed-length operations, including slicing, while deques are much more useful for pushing and popping things off the ends, with indexing (but not slicing, interestingly) being possible but slower than with lists.
Check out collections.deque. From the docs:
Deques support thread-safe, memory efficient appends and pops from either side of the deque with approximately the same O(1) performance in either direction.
Though list objects support similar operations, they are optimized for fast fixed-length operations and incur O(n) memory movement costs for pop(0) and insert(0, v) operations which change both the size and position of the underlying data representation.
Just as it says, using pop(0) or insert(0, v) incur large penalties with list objects. You can't use slice/index operations on a deque, but you can use popleft/appendleft, which are operations deque is optimized for. Here is a simple benchmark to demonstrate this:
import time
from collections import deque
num = 100000
def append(c):
for i in range(num):
c.append(i)
def appendleft(c):
if isinstance(c, deque):
for i in range(num):
c.appendleft(i)
else:
for i in range(num):
c.insert(0, i)
def pop(c):
for i in range(num):
c.pop()
def popleft(c):
if isinstance(c, deque):
for i in range(num):
c.popleft()
else:
for i in range(num):
c.pop(0)
for container in [deque, list]:
for operation in [append, appendleft, pop, popleft]:
c = container(range(num))
start = time.time()
operation(c)
elapsed = time.time() - start
print "Completed %s/%s in %.2f seconds: %.1f ops/sec" % (container.__name__, operation.__name__, elapsed, num / elapsed)
Results on my machine:
Completed deque/append in 0.02 seconds: 5582877.2 ops/sec
Completed deque/appendleft in 0.02 seconds: 6406549.7 ops/sec
Completed deque/pop in 0.01 seconds: 7146417.7 ops/sec
Completed deque/popleft in 0.01 seconds: 7271174.0 ops/sec
Completed list/append in 0.01 seconds: 6761407.6 ops/sec
Completed list/appendleft in 16.55 seconds: 6042.7 ops/sec
Completed list/pop in 0.02 seconds: 4394057.9 ops/sec
Completed list/popleft in 3.23 seconds: 30983.3 ops/sec
A deque is a generalization of stack and a queue (It is short for "double-ended queue").
Thus, the pop() operation still causes it to act like a stack, just as it would have as a list. To make it act like a queue, use the popleft() command. Deques are made to support both behaviors, and this way the pop() function is consistent across data structures. In order to make the deque act like a queue, you must use the functions that correspond to queues. So, replace pop() with popleft() in your second example, and you should see the FIFO behavior that you expect.
Deques also support a max length, which means when you add objects to the deque greater than the maxlength, it will "drop" a number of objects off the opposite end to maintain its max size.
I'll add my two cents as I was searching for this exact question but more from the time complexity involved and what should be the preferred choice for a queue implementation in Python.
As per the docs:
Deques support thread-safe, memory efficient appends and pops from either side of the deque with approximately the same O(1) performance in either direction.
This means you can use dequeues as a stack(Last in First out) and queue(First in First out) implementation with pop() or popleft() operation in O(1).
Again from docs
Though list objects support similar operations, they are optimized for fast fixed-length operations and incur O(n) memory movement costs for pop(0) and insert(0, v) operations which change both the size and position of the underlying data representation.
However, using the list as a queue requires popping from the 0th index which will cause data to be shifted resulting in O(N) operation. So if you want to use a queue for a time sensitive operation (production code or competitive programming) always use dequeue for queue implementation.
I have a performance critical application where using a bidirectional stack would be advantageous. Python's default list is often used as a general stack where append() and pop() methods are used to insert and retrieve data. Python's collections module includes an object known as 'deque' which acts as a bidirectional stack allowing append and pop methods on the 'right hand' side, mirroring behavior and methods of the default list, and on the left hand side.
This collection class lacks many of the nicer methods of list such as slice indexes, but in stack oriented applications, I had heard that this object has performance characteristics that are desirable. To verify these claims. I wrote some timing tests and I pasted the code here
My tests are written to be compatible with python 2.7 and 3.x, but I have only run in 2.7. Anybody who is willing to contribute a 3.x test would be my hero.
As it stands:
for len=2 and iter = 1000000 list pop time = 2.528 for len=2 and iter = 1000000 deque pop time = 2.085 for len=2 and iter = 1000000 deque popL time = 2.054 for len=2 and iter = 1000000 deque popH time = 1.963 for len=200000 and iter = 10 list pop time = 1.2103 for len=200000 and iter = 10 deque pop time = 0.9893 for len=200000 and iter = 10 deque popL time = 1.0090 for len=200000 and iter = 10 deque popH time = 0.99811 for len=5000000 and iter = 1 list pop time = 3.1540 for len=5000000 and iter = 1 deque pop time = 2.4654 for len=5000000 and iter = 1 deque popL time = 2.440 for len=5000000 and iter = 1 deque popH time = 2.4685 for len=50000000 and iter = 1 list pop time = 34.17 for len=50000000 and iter = 1 deque pop time = 26.83 for len=50000000 and iter = 1 deque popL time = 28.66 for len=50000000 and iter = 1 deque popH time = 27.93
We can generally conclude, in python 2.7, that deques perform ~20% - 25% percent faster for both large and short stacks. This holds up a both extreme ends use cases.
edit: without going into too much detail, I'll say that I wrote a test where the lists are iterated 100 fold after creation. I found that for this read-heavy application, deques have a ~5-10 percent performance penalty.
Hi all. I recently got stumped on an advent of code challenge, as my naive list-based solution was just not solving due to time-complexity issues. I looked on the subreddit and someone had outlined a solution using a deque. I read up a bit on the idea and the architecture, but I can't find much information about practical uses, other than when you have elements in a "circular" type list.
Anyone have a good explanation or link to a good resource explaining when it would be good to use these? They seem like a really useful tool!