Beginner’s Guide to Python Deque

Travis Cuzick
Travis Cuzick
hero image

Have you ever noticed that your Python code starts slowing down as it’s removing items from the front of a list?

If so, you probably wondered exactly why this is happening - is your computer just lagging, or is the code itself the problem?

Well don’t worry, because in this guide I’ll break down what causes this issue, and how to fix it so your code runs like clockwork.

Sidenote: If you find any of this confusing, or simply want a deep dive into Python, check out Andrei's Python Coding course taken by 200,000+ people:

It’ll take you from an absolute beginner to understanding everything you need to be hired ASAP.

Alternatively, if you're already pretty good at Python and want to build some interesting and useful projects, why not check out my course on Python Automation:

It'll show you how to automate all of the boring or repetitive tasks in your life - and makes for some pretty standout portfolio projects as well!

With all that out of the way, let's get into this guide.

Why do we need deque?

When Python creates a list, it lines up all your data in one long row in memory. You can imagine it like a row of boxes, each holding one item.

['Box 1', 'Box 2', 'Box 3', 'Box 4', 'Box 5']

One of the main benefits of using lists is that Python knows what each of these boxes contains and where it is. This means it's fairly easy and fast to jump straight to any one location and grab the data inside.

But sometimes you need to remove one of those boxes altogether, and that's where some issues can appear.

For example

Let's say that you built a task tracking app, and the user has 5 tasks they need to do:

[‘Task 1', Task 2', Task 3', Task 4', Task 5']

Then, once they complete the first task on their to-do list, we’ll need to remove that from their list in the backend like so:

[‘ ', Task 2', Task 3', Task 4', Task 5']

The issue here is that Python doesn’t like to leave gaps in rows of data, so when that person completes that first task, Python will then move all the remaining tasks up to fill out that gap. This means that Task 2 is now in Task 1's spot, and so on.

[Task 2', Task 3', Task 4', Task 5']

Now this is fine for small lists, and you probably won’t even notice it happening. However, if you have a larger list, then you’re going to have a problem because the data is still going to be handled the same way. 

That means if you have thousands of items in a list, then they’re all being moved one by one every time you need to remove one of them. This is a memory-intensive process, as you might imagine, and that’s why you may experience the kind of lag that we discussed earlier.

The good news is that there’s a solution, which is where deque comes in.

How deque works

deque (short for double-ended queue) changes how Python manages data behind the scenes.

How?

Well, instead of storing everything in one long row that needs shifting, a deque stores data in linked chunks. This lets it add or remove items from either end instantly. 

Now don’t worry if this all sounds a bit abstract. The most important thing to understand is that instead of moving the data forward to reach the front, it simply moves where the front is. Meaning it bypasses the issue of needing to reposition all the items in the list.

A good way to think of this is like getting drive-through at In-N-Out.

On a slow day, the drive-through works as normal, and all the cars queue up as usual.

  • You place your order

  • You move forward

  • And the next cars all move up one space

If you think about it, this is kind of similar to what we were saying earlier about data moving forward in a list, as the first task gets removed.

However, this drive-through process changes slightly when it's busy.

In this situation, they don’t just rely on the normal queue because it’s too slow. So instead, they send someone outside with a tablet to take orders from cars in the line. 

This helps more people to order because rather than waiting to get to the front of the queue, the ‘front’ of the queue moves to them. And that’s similar to what a deque does. 

It moves the pointer, and not the data so that the ‘front’ moves instead. All while bypassing the whole “data having to move” issue.

Smart right?

And sure, this is a bit of a simplification, but you get the idea. (And honestly, you probably wouldn’t use deque on a smaller list unless you were building with the goal of scaling up later).

In fact, the main places you’ll use deque are in large-scale or performance-sensitive systems where items are constantly being added and removed in FIFO (first-in, first-out) or LIFO (last-in, first-out) order.

Things like:

  • Task queues or job schedulers - systems where you’re constantly adding new jobs to the end and removing completed ones from the front. For example, a web crawler or background job runner might manage thousands of URLs or tasks per second.

  • Streaming data pipelines where you need to handle incoming packets of data, logs, or messages in real time, where every new item needs to be processed in the order it arrived

  • Undo/redo functionality where you keep a stack of actions that need to be popped off or pushed back quickly

  • Caching systems (like LRU caches) where you need to evict the oldest item once the cache fills up, which is a perfect use for popleft()

The bottom line is that deque is a simple tool that can nonetheless be very handy in certain situations.

However, like anything with coding, the trick is knowing how to use it properly, so let’s get into that next.

How to use deque in Python

Rather than just listing the features of deque, let’s imagine we’re building a simple

Now obviously we won’t build the full system, but we’ll go through how each feature of deque would power it behind the scenes when a user adds, completes, or reprioritizes jobs. 

This way it’s easier to grasp concepts and see how you might use it yourself.

How to import deque

Before we can start using a deque, we first need to import it from Python’s built-in collections module:

from collections import deque

Next, we’ll create an empty deque to store our pending jobs:

tasks = deque()
print(tasks)
# deque([])

Right now, our queue is empty because nothing’s waiting to be processed yet. So let’s fix that by adding some jobs to it using our first feature.

How to add new jobs with append()

Every time a user does something that triggers background work (like uploading a file or requesting a report), we’ll need to push that new job to the end of our queue. That’s what append() is for.

Each time we call append(), a new job gets added to the queue in the order it arrived, just like tasks lining up to be processed:

jobs.append('Process uploaded images')
jobs.append('Send user confirmation email')
jobs.append('Generate daily report')

And if we check what’s currently waiting, we’d see:

print(jobs)
# deque(['Process uploaded images', 'Send user confirmation email', 'Generate daily report'])

Nice and simple. 

But what if we wanted to add in multiple jobs at once?...

How to add multiple jobs with extend()

Sometimes, new jobs don’t come in one at a time. They arrive in batches, such as daily maintenance tasks, queued emails, or log files waiting to be processed overnight. 

In such cases, instead of calling append() again and again, we can add the jobs in these batches in a single go using extend().

For example

Let’s say that we already have a queue of 3 jobs to be done:

  • Process uploaded images

  • Send user confirmation email

  • And Generate daily report 

However, our system also received three new jobs that need to be added to the queue

  • Clean temp files

  • Backup database

  • And Generate analytics report

By using extend(), we can add them all at once like so:

batch_jobs = ['Clean temp files', 'Backup database', 'Generate analytics report']
jobs.extend(batch_jobs)

Easy right? 

And if we print the current queue now, it would look like this, with all 6 tasks included:

print(jobs)
# deque(['Process uploaded images', 'Send user confirmation email', 'Generate daily report', 'Clean temp files', 'Backup database', 'Generate analytics report'])

This makes it super easy to just add in additional tasks. It’s worth noting however, that each of these new tasks is added to the end of the current list, which is usually fine.

Unless of course your new task needs to be done first before everything else…

How to add high-priority jobs with appendleft()

Every now and then, an urgent job pops up that can’t wait its turn, such as a security alert or a system failure. In that case, we don’t want to tack it onto the end of the queue, because we want it processed first.

That’s what appendleft() is for. It places the new job right at the front of the deque so it’s handled next.

For example

If a system alert comes in that needs immediate attention, we could do this:

jobs.appendleft('Handle critical server alert')

Now, if we check the queue again, you’ll see that this urgent job has jumped to the front:

print(jobs)
# deque(['Handle critical server alert', 'Process uploaded images', 'Send user confirmation email', 'Generate daily report', 'Clean temp files', 'Backup database', 'Generate analytics report'])

This gives us more fine-grained control of the queue; we can still process jobs in order, but with the flexibility to prioritize urgent ones instantly.

Simple!

But what if you have multiple high-priority jobs that all need to be pushed to the front at once? Well, there’s a feature for that too!

How to add multiple high-priority jobs with extendleft()

Sometimes one urgent job turns into several. Maybe the system just logged multiple security alerts or queued up several emergency patches that need to run before anything else. 

For example

Let’s say we have 3 tasks:

  • Notify the admin team

  • Restart database service

  • Apply emergency patch

Instead of adding each one individually with appendleft(), we can use extendleft() to push them all to the front in one go.

priority_jobs = ['Notify admin team', 'Restart database service', 'Apply emergency patch']
jobs.extendleft(priority_jobs)

Easy right?

Well, there is actually a slight complication in that appendleft() adds each of these tasks to the front, but in reverse order as it works through them.

This means that the task priorities will be reversed within the batch of tasks you’re adding - for example,  ‘Notify the admin team’ would end up being 1st in the queue instead of 3rd.

print(jobs)
# deque(['Apply emergency patch', 'Restart database service', 'Notify admin team', 'Handle critical server alert', 'Process uploaded images', 'Send user confirmation email', 'Generate daily report', 'Clean temp files', 'Backup database', 'Generate analytics report'])

Sometimes this might not be an issue. However, if you need them to appear in the correct order, you can simply reverse the list before calling extendleft() like so:

jobs.extendleft(reversed(priority_jobs))

This will then reverse them first, then reverse them again when added, resulting in them being added in the correct order. 

print(jobs)
# deque(['Notify admin team', 'Restart database service', 'Apply emergency patch', 'Handle critical server alert', 'Process uploaded images', 'Send user confirmation email', 'Generate daily report', 'Clean temp files', 'Backup database', 'Generate analytics report'])

While this may be a bit of a pain, if you need to add multiple items to the front of a queue in a specific order, it’s an important technique to understand.

Now another important thing - aside from all these methods for adding items to queues - is removing them. So let’s get into that next.

How to remove completed jobs with popleft()

Once a job finishes running, we can remove it from the front of the queue using popleft().

jobs.popleft()

This removes the oldest job instantly. But what if you want to remove the newest job instead?

Well, we can do that too!

How to remove canceled or unneeded jobs with pop()

Not every job in a queue makes it to the finish line. Sometimes a user cancels a request before it runs, or an automated process finishes early and no longer needs the follow-up task. 

Obviously we don’t want to keep tasks in our queue if they’re not needed, so we can use pop() to remove them.

For example

Let’s say that this is our current queue of tasks:


print(jobs)
# deque(['Process uploaded images', 'Send user confirmation email', 'Generate daily report', 'Clean temp files', 'Backup database', 'Generate analytics report'])

We can then run:

jobs.pop()

And as you can see, the last job disappears from the queue entirely:

print(jobs)
# deque(['Process uploaded images', 'Send user confirmation email', 'Generate daily report', 'Clean temp files', 'Backup database'])

Easy!

So now that we’ve seen how to add and remove jobs, let’s look at how to reshuffle the order of everything that’s left with rotate().

How to reshuffle the queue with rotate()

Sometimes you don’t want to add or remove jobs, you just want to adjust the order of them. 

  • Perhaps one task got delayed and needs to move to the end

  • Or you want to bump an older job back to the front for quicker processing

  • Or maybe you want to retry failed jobs by cycling them back to the front of the queue

Well, that’s where rotate() comes in and it’s a great way to reshuffle tasks without manually removing and re-adding them. A positive number moves items from the end to the front, while a negative number moves items from the front to the end.

For example

Let’s say that our current queue looks like this:

print(jobs)
# deque(['Process uploaded images', 'Send user confirmation email', 'Generate daily report', 'Clean temp files', 'Backup database'])

If we call rotate(1), then the last job (‘Backup Database') will move to the front to be done first.

jobs.rotate(1)
print(jobs)
# deque(['Backup database', 'Process uploaded images', 'Send user confirmation email', 'Generate daily report', 'Clean temp files'])

While if we call rotate(-1), the opposite happens: the first job moves to the end instead:

jobs.rotate(-1)
print(jobs)
# deque(['Process uploaded images', 'Send user confirmation email', 'Generate daily report', 'Clean temp files', 'Backup database'])

Easy!

So now that we’ve covered how to add, remove, and reshuffle jobs, let’s finish things off by looking at how to completely clear the queue when everything’s done.

How to clear the queue with clear()

Eventually, every queue reaches the end of its life cycle. Maybe all the jobs have been processed successfully, or perhaps a system restart requires wiping the queue and starting fresh.

Whatever the reason, when you want to remove everything from your deque at once, you can use clear().

For example

Let’s say our current queue still has a few jobs left:

print(jobs)
# deque(['Process uploaded images', 'Send user confirmation email', 'Generate daily report', 'Clean temp files', 'Backup database'])

Well, if we call clear() then everything gets wiped instantly:

jobs.clear()
print(jobs)
# deque([])

And just like that, we now have an empty queue, ready to start over.

clear() is especially useful for resetting your system after maintenance or tests, to help ensure no leftover jobs accidentally run later.

Give deque a try in your own code today!

As you can see, deque is the perfect solution for lists that are getting too large to handle. 

So if you’ve ever noticed your own code lagging when removing or adding lots of items, make sure to give deque a try. You’ll instantly see the difference in speed and simplicity.

Or just have a play around! The best way to learn new concepts is to put them into action, so open up some projects and see what you can do!

P.S.

Don’t forget, if you want to learn more and dive deep into Python, then be sure to check out Andrei's Complete Python Developer course

It’ll take you from an absolute beginner and teach you everything you need to get hired ASAP and ace the tech interview.

This is the only Python course you need if you want to go from a complete Python beginner to getting hired as a Python Developer this year!

Alternatively, if you're already pretty good at Python and want to build some interesting and useful projects, why not check out my course on Python Automation:

It'll show you how to automate all of the boring or repetitive tasks in your life, and makes for some pretty stand out portfolio projects!

Plus, as part of your membership, you'll get access to both of these courses and others, and be able to join me and 1,000s of other people (some who are alumni mentors and others who are taking the same courses that you will be) in the ZTM Discord.

Ask questions, help others, or just network with other Python Developers, students, and tech professionals.

PDF Download Of This Learn Python For Free Guide

If you enjoyed Travis's post and want to get more like it in the future, subscribe below. By joining over 400,000 ZTM email subscribers, you'll receive exclusive ZTM posts, opportunities, and offers.

No spam ever, unsubscribe anytime

More Python Tutorials

If you enjoyed this post, check out my other Python tutorials:

More from Zero To Mastery

Beginner’s Guide To Python Automation Scripts (With Code Examples) preview
Beginner’s Guide To Python Automation Scripts (With Code Examples)
24 min read

Use Python automation scripts to save time, reduce errors, and boost productivity. Perfect for all skill levels. Work smarter, not harder!

How to Become a Full-Stack Web Developer & Get Hired in 2025 preview
How to Become a Full-Stack Web Developer & Get Hired in 2025
32 min read

Learn everything you need to know to become a Full-Stack Web Developer, as well as how to get hired as one in 2025 with this step-by-step guide!

Python Interview Questions preview
Python Interview Questions
15 min read

Are you taking a coding interview with Python? On rare occasions, you may be asked broad language questions before the technical interview: Here's 25 of them.