Почему Python Queue не ведет себя фальшиво - PullRequest
1 голос
/ 01 октября 2019

В Python я довольно привык к контейнерам объектов, имеющих правдивое поведение, когда они заполнены, и ложное поведение, когда их нет:

# list
a = []
not a
True

a.append(1)
not a
False

# deque
from collections import deque
d = deque()
not d
True

d.append(1)
not d
False

# and so on

Однако queue.Queue не имеет такого поведения. Мне это кажется странным и противоречит практически любому другому типу данных контейнера, о котором я могу думать. Кроме того, метод empty в очереди, кажется, идет вразрез с соглашениями о кодировании, которые избегают условий гонки для любого другого объекта (проверка, существует ли файл, проверка, пустой ли список, и т. Д.). Например, мы обычно говорим, что следующее - это плохая практика:

_queue = []

if not len(_queue):
    # do something

И должно быть заменено на

_queue = []

if not _queue:
    # do something

или для обработки IndexError, что, как мы все еще можем утверждать, будетлучше с оператором if not _queue:

try:
    x = _queue.pop()
except IndexError as e:
    logger.exception(e)
    # do something else

Тем не менее, Queue требует, чтобы кто-то сделал одно из следующих действий:

_queue = queue.Queue()

if _queue.empty():
    # do something
    # though this smells like a race condition


# or handle an exception
try:
    _queue.get(timeout=5)
except Empty as e:
    # do something else
    # maybe logger.exception(e)

Есть ли где-нибудь документация, которая может указывать на почему этот выбор дизайна был сделан? Это кажется странным, особенно когда исходный код показывает, что он был построен поверх collections.deque (отмечается, что очередь не наследуется от deque)

Ответы [ 2 ]

3 голосов
/ 01 октября 2019

В соответствии с определением процедуры проверки истинности , ожидается следующее поведение:

Любой объект может быть проверен на значение истинности для использования в операциях if или whileусловие или как операнд булевых операций ниже.

По умолчанию объект считается истинным, если только его класс не определяет либо метод __bool__(), который возвращает False, либо метод __len__(), который возвращает ноль, когда вызывается собъект.

Поскольку Queue не реализует ни __bool__(), ни __len__(), тогда ее истинное значение равно True. Относительно того, почему Queue не реализует __len__(), ключ можно найти в комментариях к функции qsize:

'' 'Возвращает приблизительный размер очереди (не надежно!).'''

То же самое можно сказать о функции __bool__().

0 голосов
/ 02 октября 2019

Я собираюсь оставить принятый ответ как есть, но, насколько я могу судить, причина в том, что if _queue: # do something будет условием гонки, поскольку Queue предназначено дляпередается между потоками и поэтому обладает сомнительным состоянием в отношении задач.

Из источника:

class Queue:
    ~snip~

    def qsize(self):
        '''Return the approximate size of the queue (not reliable!).'''
        with self.mutex:
            return self._qsize()


    def empty(self):
        '''Return True if the queue is empty, False otherwise (not reliable!).
        This method is likely to be removed at some point.  Use qsize() == 0
        as a direct substitute, but be aware that either approach risks a race
        condition where a queue can grow before the result of empty() or
        qsize() can be used.
        To create code that needs to wait for all queued tasks to be
        completed, the preferred technique is to use the join() method.
        '''
        with self.mutex:
            return not self._qsize()

~snip

Должно быть, пропустил эту полезную строку документации, когда я первоначально искал. Bool qsize не привязан к состоянию очереди после его оценки. Таким образом, пользователь выполняет обработку в очереди на основе уже устаревшего состояния.

Подобно проверке существования файла, более логично обрабатывать исключение:

try:
    task = _queue.get(timeout=4)
except Empty as e:
    # do something

поскольку исключение / успех для get является состоянием очереди.

Аналогично, мы не будем делать:

if os.exists(file):
    with open(file) as fh:
        # do processing

Вместо этого мы быdo:

try:
    with open(file) as fh:
        # do processing
except FileNotFoundError as e:
    # do something else

Я полагаю, что намеренное исключение автором метода __bool__ направляет разработчика на то, чтобы он не опирался на такую ​​парадигму и не рассматривал очередькак и любой другой объект, который может иметь сомнительное состояние.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...