Python: Управление очередью Queue.Empty

После коротких дискуссий с кем-то об обработке исключений в Python, вызванных обработкой объекта очереди, я подумал, что выброшу его там...

МЕТОД 1:

import Queue

q = Queue.Queue()

try:
    task=q.get(False)
    #Opt 1: Handle task here and call q.task_done()
except Queue.Empty:
    #Handle empty queue here
    pass

#Opt2: Handle task here and call q.task_done()

МЕТОД 2:

import Queue

q = Queue.Queue()

if q.empty():
    #Handle empty queue here
else:
    task = q.get()
    #Handle task here
    q.task_done()

Один аргумент состоит в том, что метод 1 неверен, поскольку пустая очередь не является ошибкой и поэтому не должна обрабатываться с использованием исключения Queue.Empty. Кроме того, это может затруднить отладку при кодировании таким образом, если вы считаете, что часть обработки задачи потенциально может быть большой.

Другой аргумент заключается в том, что любой способ приемлем в Python и что обработка задачи за пределами try/except может помочь отладке, если обработка задачи велика, хотя и согласилась, что это может выглядеть более уродливым, чем использование метода 2.

мнения?

ОБНОВЛЕНИЕ: немного больше информации после ответа 1... Обсуждение началось после того, как метод 1 использовался в некотором многопоточном коде. В этом случае код получит блокировку (из объекта threading.Lock) и отпустит ее либо после возвращения задачи, либо из очереди Queue.Empty.

ОБНОВЛЕНИЕ 2: Нам было неизвестно, что объект очереди был потокобезопасным. Похоже, что try/except - это путь!

Ответы

Ответ 1

Метод 2 неверен, потому что вы выполняете операцию в два этапа, когда это можно сделать в одном. В методе 2 вы проверяете, является ли очередь пустой, а затем позже (очень скоро, но еще позже) попробуйте получить элемент. Что делать, если у вас есть два потока, вытягивающих предметы из очереди? Функция get() все равно может работать с пустой очередью. Что делать, если элемент добавлен в очередь после того, как вы проверили, что он пуст? Это своего рода крошечные окна возможностей, где ошибки вступают в параллельный код.

Сделайте это за один шаг, это лучший выбор.

import Queue

q = Queue.Queue()

try:
    task = q.get(False)
except Queue.Empty:
    # Handle empty queue here
    pass
else:
    # Handle task here and call q.task_done()

Не зацикливайтесь на "исключениях должны быть ошибки". Исключения - это просто еще один канал коммуникации, используйте их. Используйте предложение "else" здесь, чтобы сузить область действия предложения исключения.

Ответ 2

Если это многопоточный/многопроцессорный код (так или иначе, это хорошая причина для использования очередей), то определенно метод 1. Между вызовом q.empty() и вызовом q.get() Джек Сердца мог украсть ваши пироги!

Ответ 3

Один аргумент заключается в том, что метод 1 неверен, поскольку пустая очередь не является ошибкой и поэтому не должна обрабатываться с использованием исключения Queue.Empty

Исключение не обязательно является "ошибкой", это общий механизм управления потоком и действительно используется в нескольких случаях (SysExit, StopIteration и т.д.).

Хороший вопрос: какой будет самый распространенный случай - пустая или непустая очередь. Если вы точно не знаете, вы хотите, чтобы AskBeforeYouLeap, потому что это очень вероятно дешевле.