← Назад к вопросам
В чём разница между queue и deque в collections?
2.0 Middle🔥 181 комментариев
#Python Core
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Разница между queue и deque в collections
В Python модуль collections предоставляет deque, а модуль queue — класс Queue. Это разные структуры данных для разных задач.
deque (Double-Ended Queue)
deque — это структура данных из модуля collections, которая позволяет добавлять и удалять элементы с обоих концов очень быстро.
from collections import deque
# Создание
d = deque([1, 2, 3, 4, 5])
print(d) # deque([1, 2, 3, 4, 5])
# Добавить в конец — O(1)
d.append(6)
print(d) # deque([1, 2, 3, 4, 5, 6])
# Добавить в начало — O(1)
d.appendleft(0)
print(d) # deque([0, 1, 2, 3, 4, 5, 6])
# Удалить с конца — O(1)
d.pop() # Вернёт 6
print(d) # deque([0, 1, 2, 3, 4, 5])
# Удалить с начала — O(1)
d.popleft() # Вернёт 0
print(d) # deque([1, 2, 3, 4, 5])
# Обращение (поворот)
d.rotate(2) # Сдвигает на 2 позиции вправо
print(d) # deque([4, 5, 1, 2, 3])
Операции deque:
| Операция | Сложность | Описание |
|---|---|---|
| append(x) | O(1) | Добавить в конец |
| appendleft(x) | O(1) | Добавить в начало |
| pop() | O(1) | Удалить с конца |
| popleft() | O(1) | Удалить с начала |
| rotate(n) | O(n) | Повернуть на n позиций |
| [i] | O(n) | Доступ по индексу |
Когда использовать deque:
- Скользящее окно
- Структуры данных, требующие быстрые операции с обоих концов
- Реализация стека и очереди
# Пример: скользящее окно (moving average)
from collections import deque
def moving_average(values, window_size=3):
d = deque(maxlen=window_size)
for value in values:
d.append(value)
if len(d) == window_size:
yield sum(d) / window_size
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9]
for avg in moving_average(numbers, 3):
print(avg) # 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0
Queue (из модуля queue)
Queue — это потокобезопасная очередь из модуля queue. Это синхронизированная структура данных для многопоточных приложений.
from queue import Queue
# Создание
q = Queue(maxsize=5)
# Добавить элемент
q.put(1)
q.put(2)
q.put(3)
# Получить элемент (FIFO)
print(q.get()) # 1
print(q.get()) # 2
print(q.get()) # 3
# Проверка пустоты
print(q.empty()) # True
# Размер (примерно, не гарантирован в многопоточности)
print(q.qsize()) # 0
Особенности Queue:
| Характеристика | Описание |
|---|---|
| Thread-safe | Да, использует locks |
| Операции | put(), get(), put_nowait(), get_nowait() |
| Блокировка | Может блокировать, если full/empty |
| FIFO | Да (First In First Out) |
| maxsize | Ограничивает размер |
Когда использовать Queue:
- Многопоточные приложения
- Производитель-потребитель паттерн
- Обработка задач в отдельных потоках
import threading
from queue import Queue
def producer(q):
for i in range(5):
q.put(i)
print(f"Produced: {i}")
def consumer(q):
while True:
item = q.get()
if item is None:
break
print(f"Consumed: {item}")
q.task_done()
q = Queue()
# Запускаем потоки
t1 = threading.Thread(target=producer, args=(q,))
t2 = threading.Thread(target=consumer, args=(q,))
t1.start()
t2.start()
t1.join()
q.put(None) # Сигнал к завершению
t2.join()
Прямое сравнение
| Аспект | deque | Queue |
|---|---|---|
| Модуль | collections | queue |
| Thread-safe | Нет | Да |
| Назначение | Одноротовая | Многопоточность |
| FIFO | Да | Да |
| LIFO | Да (как стек) | Нет |
| Две стороны | Да (append/appendleft) | Нет |
| Блокирующие операции | Нет | Да (put/get) |
| Производительность | Очень быстро | Медленнее (из-за locks) |
Практические примеры
1. Очередь задач с deque (однопоточный)
from collections import deque
import time
class TaskQueue:
def __init__(self):
self.tasks = deque()
def add_task(self, task):
self.tasks.append(task)
def process_all(self):
while self.tasks:
task = self.tasks.popleft()
task()
time.sleep(0.1)
def task1():
print("Task 1")
def task2():
print("Task 2")
q = TaskQueue()
q.add_task(task1)
q.add_task(task2)
q.process_all()
2. BFS (поиск в ширину) с deque
from collections import deque
def bfs(graph, start):
visited = set()
queue = deque([start])
visited.add(start)
while queue:
node = queue.popleft()
print(node)
for neighbor in graph[node]:
if neighbor not in visited:
queue.append(neighbor)
visited.add(neighbor)
graph = {
'A': ['B', 'C'],
'B': ['D'],
'C': ['E'],
'D': [],
'E': []
}
bfs(graph, 'A') # A, B, C, D, E
3. Многопоточная очередь с Queue
import threading
from queue import Queue
import time
def worker(q, worker_id):
while True:
item = q.get()
if item is None:
break
print(f"Worker {worker_id}: processing {item}")
time.sleep(1)
q.task_done()
q = Queue()
threads = []
# Создаём 3 работника
for i in range(3):
t = threading.Thread(target=worker, args=(q, i))
t.start()
threads.append(t)
# Добавляем задачи
for i in range(10):
q.put(f"Task-{i}")
# Ждём завершения
q.join()
# Останавливаем работников
for _ in threads:
q.put(None)
for t in threads:
t.join()
4. Очередь приоритета с Queue
from queue import PriorityQueue
import threading
pq = PriorityQueue()
# Элементы с приоритетом (priority, item)
pq.put((1, 'Low priority'))
pq.put((0, 'High priority'))
pq.put((2, 'Low priority 2'))
while not pq.empty():
priority, item = pq.get()
print(f"Priority {priority}: {item}")
Производительность
from collections import deque
from queue import Queue
import time
# Тест deque (однопоточный)
start = time.time()
d = deque()
for i in range(100000):
d.append(i)
while d:
d.popleft()
print(f"deque time: {time.time() - start:.3f}s") # ~0.01s
# Тест Queue (однопоточный)
start = time.time()
q = Queue()
for i in range(100000):
q.put(i)
while not q.empty():
q.get()
print(f"Queue time: {time.time() - start:.3f}s") # ~0.5s
deque быстрее в 50 раз! Queue медленнее из-за использования locks для потокобезопасности.
LifoQueue и другие варианты
from queue import Queue, LifoQueue, PriorityQueue
# FIFO (обычная очередь)
fifo = Queue()
fifo.put(1); fifo.put(2)
print(fifo.get()) # 1
# LIFO (стек)
lifo = LifoQueue()
lifo.put(1); lifo.put(2)
print(lifo.get()) # 2
# С приоритетом
pq = PriorityQueue()
pq.put((1, 'first')); pq.put((0, 'zero'))
print(pq.get()) # (0, 'zero')
Заключение
- deque: для быстрых операций с обоих концов очереди в однопоточном коде
- Queue: для безопасной обработки задач в многопоточных приложениях
Выбирай deque если работаешь в одном потоке и нужна скорость. Выбирай Queue если работаешь с несколькими потоками и нужна синхронизация.