Python deque append: Syntax and Performance
Learn how python deque append works, why it is O(1), and when to use it instead of a list for queues, sliding windows, and bounded buffers.
python deque append requires a clear understanding of the core syntax, runtime behavior, and practical implementation patterns demonstrated in the examples below.
Python's deque.append adds an element to the right end of a double-ended queue in constant time, O(1). This single method is why deques are the default choice for FIFO queues, sliding windows, and bounded buffers in Python.
What deque.append Does
The deque class lives in Python's collections module. Its append method inserts one element at the right end of the queue, preserving insertion order:
from collections import deque tasks = deque() tasks.append("process_payment") tasks.append("send_email")
After these calls, tasks holds ["process_payment", "send_email"]. The method returns None; it mutates the deque in place. Every call runs in constant time regardless of how many elements the deque already contains. That O(1) guarantee is the defining property of the operation.
Why deque.append Is Not list.append
A Python list stores elements in a contiguous memory buffer. Appending to the end of a list is amortized O(1) because the buffer can grow when needed. Inserting at the front, list.insert(0, value), requires shifting every existing element one position, making it O(n).
A deque is implemented as a doubly linked list of fixed-size memory blocks. Appending to either end touches only the block at that end, so both append and appendleft complete in O(1):
from collections import deque buffer = deque() buffer.append("right") buffer.appendleft("left")
The cost is random access. Reading buffer[5000] requires walking the linked structure and is O(n). If your code frequently reads elements by position, a list is the better container.
Appending with a Maximum Length
The deque constructor accepts a maxlen argument. When the deque is at capacity, appending a new element silently discards the element at the opposite end:
from collections import deque recent = deque(maxlen=3) recent.append("a") recent.append("b") recent.append("c") recent.append("d") print(list(recent)) # ['b', 'c', 'd']
This is useful for keeping a fixed-size history, such as the last few log lines or the most recent user actions. The append remains O(1) even when the deque is full; the eviction of the leftmost element is part of the same constant-time operation.
Once maxlen is set, it cannot be changed on an existing deque. If you need a different capacity later, create a new deque.
Appending Multiple Elements at Once
The extend method appends an iterable to the right end. extendleft appends to the left end, but the resulting order is reversed relative to the input because each element is inserted one at a time at the left:
from collections import deque d = deque() d.extend([1, 2, 3]) print(list(d)) # [1, 2, 3] d.extendleft([4, 5]) print(list(d)) # [5, 4, 1, 2, 3]
extendleft([4, 5]) first inserts 4 at the left, then inserts 5 to the left of 4, so 5 ends up at the front. If you need the left-side elements to appear in the same order as the input, reverse the sequence before calling extendleft.
Performance Characteristics and When They Matter
The O(1) append behavior matters when elements are added and removed from both ends continuously. A breadth-first search over a graph is a typical case: nodes are appended to the right and popped from the left.
from collections import deque def bfs(graph, start): visited = set() queue = deque([start]) while queue: node = queue.popleft() if node in visited: continue visited.add(node) for neighbor in graph[node]: queue.append(neighbor) return visited
Using a list for queue would make pop(0) an O(n) operation, turning the traversal into O(V²) in the worst case for a dense graph. The deque keeps both append and popleft at O(1), so the traversal stays linear in the number of edges.
The same reasoning applies to sliding-window algorithms, task queues, and undo histories that trim from one end while adding to the other.
Thread Safety and Append
Individual append calls on a deque are atomic under the global interpreter lock (GIL). Two threads appending concurrently will not corrupt the deque's internal structure.
That guarantee does not extend to compound operations. A check-then-act sequence such as:
if len(q) < limit: q.append(item)
is not atomic. Between the len check and the append, another thread can change the deque. If you need coordinated behavior, protect the sequence with a threading.Lock, or use a queue.Queue, which provides blocking and coordination semantics on top of a deque.
Common Mistakes When Appending
A frequent mistake is assuming that deque.append returns the modified deque. It returns None, so chaining fails:
d = deque() d.append(1).append(2) # AttributeError: 'NoneType' object has no attribute 'append'
Another mistake is using a deque when indexed access dominates the workload. Deques support indexing, but each index access is O(n) in the worst case. If the code reads d[i] in a loop, a list is the appropriate structure.
A third issue is forgetting that maxlen eviction is silent. Appending to a full deque produces no error; the oldest element simply disappears. For a bounded buffer where overflow must be detected, check len(d) == d.maxlen before appending, or use a structure that raises on overflow.
When to Use deque.append vs list.append
The decision depends on what else the code does with the container. If elements are only added at the end and read by index, a list is simpler and faster for indexed reads. If elements are added at both ends, or removed from the front, a deque avoids the O(n) shift cost of a list.
For a FIFO queue where items are appended at the back and consumed from the front, the deque is the standard choice. For a stack where push and pop happen at the same end, a list is equally efficient and simpler. The deque's advantage appears specifically when both ends are active.