Search code examples
pythonpriority-queuepython-dataclasses

Using queue.PriorityQueue, not caring about comparisons


I'm trying to use queue.PriorityQueue in Python 3(.6).

I would like to store objects with a given priority. But if two objects have the same priority, I don't mind PriorityQueue.get to return either. In other words, my objects can't be compared at integers, it won't make sense to allow them to be, I just care about the priority.

In Python 3.7's documentation, there's a solution involving dataclasses. And I quote:

If the data elements are not comparable, the data can be wrapped in a class that ignores the data item and only compares the priority number:

from dataclasses import dataclass, field
from typing import Any

@dataclass(order=True)
class PrioritizedItem:
    priority: int
    item: Any=field(compare=False)

Alas, I'm using Python 3.6. In the documentation of this version of Python, there's no comment on using PriorityQueue for the priorities, not bothering about the "object value" which wouldn't be logical in my case.

Is there a better way than to define __le__ and other comparison methods on my custom class? I find this solution particularly ugly and counter-intuitive, but that might be me.


Solution

  • dataclasses is just a convenience method to avoid having to create a lot of boilerplate code.

    You don't actually have to create a class. A tuple with a unique counter value too:

    from itertools import count
    
    unique = count()
    
    q.put((priority, next(unique), item))
    

    so that ties between equal priority are broken by the integer that follows; because it is always unique the item value is never consulted.

    You can also create a class using straight-up rich comparison methods, made simpler with @functools.total_ordering:

    from functools import total_ordering
    
    @total_ordering
    class PrioritizedItem:
        def __init__(self, priority, item):
            self.priority = priority
            self.item = item
    
        def __eq__(self, other):
            if not isinstance(other, __class__):
                return NotImplemented
            return self.priority == other.priority
    
        def __lt__(self, other):
            if not isinstance(other, __class__):
                return NotImplemented
            return self.priority < other.priority