Как я могу создать в Python очередь с уникальным приоритетом значений?

В Python есть Queue.PriorityQueue, но я не вижу способа сделать каждое значение в нем уникальным, поскольку нет метода проверки того, существует ли уже значение (например, find (name) или аналогичный). Более того, PriorityQueue требует, чтобы приоритет оставался в пределах значения, поэтому я не мог даже искать свое значение, так как мне также нужно было знать приоритет. Вы должны использовать (0,5, myvalue) как значение в PriorityQueue, а затем оно будет отсортировано по первому элементу кортежа.

С другой стороны, класс collections.deque предлагает функцию для проверки того, существует ли значение уже, и он даже более естественен в использовании (без блокировки, но все еще атомарен), но не предлагает способа сортировки по приоритету.

Есть несколько других реализаций stackoverflow с heapq, но heapq также использует приоритет внутри значения (например, в первой позиции кортежа), поэтому он не подходит для сравнения уже существующих значений.

Создание очереди приоритетов Python

https://stackoverflow.com/questions/3306179/priority-queue-problem-in-python < / а>

Как лучше всего создать очередь с атомарным приоритетом (= может использоваться из нескольких потоков) с уникальными значениями?

Пример того, что я хочу добавить:

  • Приоритет: 0,2, значение: значение1
  • Приоритет: 0,3, значение: значение2
  • Приоритет: 0,1, Значение: значение3 (извлекается сначала автоматически)
  • Priority: 0.4, Value: value1 (не добавляется повторно, даже если у него другой приоритет)

person aufziehvogel    schedule 13.05.2011    source источник


Ответы (4)


Вы можете комбинировать приоритетную очередь с набором:

import heapq

class PrioritySet(object):
    def __init__(self):
        self.heap = []
        self.set = set()

    def add(self, d, pri):
        if not d in self.set:
            heapq.heappush(self.heap, (pri, d))
            self.set.add(d)

    def get(self):
        pri, d = heapq.heappop(self.heap)
        self.set.remove(d)
        return d

При этом используется очередь приоритетов, указанная в одном из связанных вопросов. Не знаю, хотите ли вы этого, но таким способом довольно легко добавить набор в любую очередь.

person Boaz Yaniv    schedule 13.05.2011
comment
Я предлагаю не использовать имена встроенных функций, такие как self.set - person sleepsort; 28.11.2013
comment
возможно, поп - лучшее название для получения :) - person DikobrAz; 20.08.2014

Вот один из способов сделать это. В основном я начал с того, как они определили PriorityQueue в Queue.py и добавили в него набор для отслеживания уникальных ключей:

from Queue import PriorityQueue
import heapq

class UniquePriorityQueue(PriorityQueue):
    def _init(self, maxsize):
#        print 'init'
        PriorityQueue._init(self, maxsize)
        self.values = set()

    def _put(self, item, heappush=heapq.heappush):
#        print 'put',item
        if item[1] not in self.values:
            print 'uniq',item[1]
            self.values.add(item[1])
            PriorityQueue._put(self, item, heappush)
        else:
            print 'dupe',item[1]

    def _get(self, heappop=heapq.heappop):
#        print 'get'
        item = PriorityQueue._get(self, heappop)
#        print 'got',item
        self.values.remove(item[1])
        return item

if __name__=='__main__':
    u = UniquePriorityQueue()

    u.put((0.2, 'foo'))
    u.put((0.3, 'bar'))
    u.put((0.1, 'baz'))
    u.put((0.4, 'foo'))

    while not u.empty():
        item = u.get_nowait()
        print item

Боаз Янив опередил меня на несколько минут, но я решил, что выложу и свой, поскольку он поддерживает полный интерфейс PriorityQueue. Я оставил некоторые операторы печати без комментариев, но закомментировал те, которые добавил во время отладки. ;)

person John Gaines Jr.    schedule 13.05.2011
comment
Спасибо за Ваш ответ. Еще не знаком с методами _init, _put и _get, но они действительно практичны при расширении очереди. И поскольку вы оба использовали наборы, теперь я убежден, что это правильный путь;) - person aufziehvogel; 14.05.2011

Если вы хотите расставить приоритеты для задачи позже.

u = UniquePriorityQueue()

u.put((0.2, 'foo'))
u.put((0.3, 'bar'))
u.put((0.1, 'baz'))
u.put((0.4, 'foo'))
# Now `foo`'s priority is increased.
u.put((0.05, 'foo'))

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

import heapq
import Queue

class UniquePriorityQueue(Queue.Queue):
    """
    - https://github.com/python/cpython/blob/2.7/Lib/Queue.py
    - https://docs.python.org/3/library/heapq.html
    """

    def _init(self, maxsize):
        self.queue = []
        self.REMOVED = object()
        self.entry_finder = {}

    def _put(self, item, heappush=heapq.heappush):
        item = list(item)
        priority, task = item
        if task in self.entry_finder:
            previous_item = self.entry_finder[task]
            previous_priority, _ = previous_item
            if priority < previous_priority:
                # Remove previous item.
                previous_item[-1] = self.REMOVED
                self.entry_finder[task] = item
                heappush(self.queue, item)
            else:
                # Do not add new item.
                pass
        else:
            self.entry_finder[task] = item
            heappush(self.queue, item)

    def _qsize(self, len=len):
        return len(self.entry_finder)

    def _get(self, heappop=heapq.heappop):
        """
        The base makes sure this shouldn't be called if `_qsize` is 0.
        """
        while self.queue:
            item = heappop(self.queue)
            _, task = item
            if task is not self.REMOVED:
                del self.entry_finder[task]
                return item
        raise KeyError('It should never happen: pop from an empty priority queue')
person colinfang    schedule 22.02.2017

Мне нравится ответ @Jonny Gaines Jr., но я думаю, что его можно упростить. PriorityQueue использует список, поэтому вы можете просто определить:

class PrioritySetQueue(PriorityQueue):
    def _put(self, item):
        if item not in self.queue:
            super(PrioritySetQueue, self)._put(item)
person Peter w    schedule 20.05.2021