39

Normally we code it like this:

while True:
    job = queue.get()
    ...

But is it also possible to do something in the lines of:

for job in queue.get():
    #do stuff to job

The real reason why I want to do that is because I wanted to use python-progressbar's auto detect maxval. They do it like for this in progressbar(that):

user2357112
  • 260,549
  • 28
  • 431
  • 505
majidarif
  • 18,694
  • 16
  • 88
  • 133

5 Answers5

65

You can use iter with callable. (You should pass two arguments, one for the callable, the other for the sentinel value)

for job in iter(queue.get, None): # Replace `None` as you need.
    # do stuff with job

NOTE This will block when no elements remain and no sentinel value is put. Also, like a while-get loop and unlike normal for loops over containers, it will remove items from the queue.

None is common value, so here's a sample with more concrete sentinel value:

sentinel = object()
for job in iter(queue.get, sentinel):
    # do stuff with job
Neuron
  • 5,141
  • 5
  • 38
  • 59
falsetru
  • 357,413
  • 63
  • 732
  • 636
  • @majimboo, Where do you put `join`? Could you update the code in the question? – falsetru Jan 16 '14 at 09:54
  • 1
    @falsetru, Ahhh, I got it. But the problem is the last part errors when there is no data anymore. Why is that? because there is no sentinel value? – majidarif Jan 16 '14 at 09:58
  • 1
    @majimboo, Add a sentinel value (`None` in the answer code) after the normal job related values. `queue.put(None)` – falsetru Jan 16 '14 at 10:00
  • @falsetru yup, seems to work. but still can't figure out my problem on how to make it auto detect max value with `https://code.google.com/p/python-progressbar/` anyways. thanks. – majidarif Jan 16 '14 at 10:03
  • @majimboo, It seems like the `python-progressbar` use the length of the iterable object. But as you can see from other answer/comment, `Queue` is not iterable, havs no `__len__`, but `qsize`. – falsetru Jan 16 '14 at 10:08
  • @majimboo, How about pass `maxval` explicitly? – falsetru Jan 16 '14 at 10:09
  • but how can I set it if I don't know how many jobs are in the queue? Some jobs are added when the job is executed. so the initial qsize is still wrong. – majidarif Jan 16 '14 at 10:56
  • @majimboo, How about post separated question? – falsetru Jan 16 '14 at 10:58
  • I will. :) but I'm still trying to figure it out on my own. – majidarif Jan 16 '14 at 11:09
  • In Python 3, this hangs for me if I call it on an empty multiprocessing.Queue object. – micahscopes Nov 17 '17 at 01:27
  • 1
    @micahscopes, I mentioned that in `NOTE` part. It's same for Python 2.x. – falsetru Nov 17 '17 at 04:30
  • @falsetru: gotcha! I wondered if that's what that was about. I just ended up using `while not q.empty(): ...` since I wasn't bound to the `for` syntax. Maybe you could elaborate on that note with an example? Not necessary but could be helpful for some people who are new to concurrent programming (me!). – micahscopes Nov 17 '17 at 21:59
  • I prefer `for elem in list(q.queue)` as it does not rely on the sentinel value: https://stackoverflow.com/a/47698422/895245 – Ciro Santilli OurBigBook.com Dec 07 '17 at 15:28
  • @CiroSantilli包子露宪六四事件法轮功 The problem with that is items can be added to the queue after the list call has already completed. – Brian M. Sheldon May 23 '18 at 20:11
  • @falsetru Instead of None as the sentinel, why not use StopIteration as that's much less likely to be found in the queue and semantically its the correct meaning. – Brian M. Sheldon May 23 '18 at 20:12
  • @BrianM.Sheldon, `None` is just example. Choose another as you want :) – falsetru May 24 '18 at 00:14
  • 1
    @falsetru True it's just an example but better to show a more appropriate sentinel value as None is usually a poor choice – Brian M. Sheldon May 27 '18 at 17:33
  • the documentation doesn't seem to be too helpful in explaining what sentinel needs to be and when and why. Do you mind adding that to your answer and make it self contained? – Charlie Parker Mar 05 '19 at 19:45
  • @CharlieParker, Added another example with non-None sentinel value. hope this helps you. Thanks. – falsetru Mar 06 '19 at 14:10
  • I don't think it is clear what 'sentinel' means. This sounds like starcraft 1 lingo/psuedo jargon. – Chris Jan 19 '22 at 19:43
  • @Chris, https://en.wikipedia.org/wiki/Sentinel_value#:~:text=In%20computer%20programming%2C%20a%20sentinel,a%20loop%20or%20recursive%20algorithm. – falsetru Jan 20 '22 at 00:12
19

I would say this is an easy way to iterate over queue in some points:

from queue import Queue

q = Queue()
q.put(1)
q.put(2)
q.put(3)

for i in q.queue:
    print(i)
shakil18
  • 190
  • 1
  • 7
  • 1
    I think it might be necessary to lock the queue if you do this using threads. You might be able to use `queue.mutex` for this, but it's not documented so I'm not 100% sure. – href_ Feb 12 '20 at 07:42
  • 1
    @href_ : no, this is wrong! `queue.Queue` is automatically thread-safe, and `mp.Queue` is automatically process safe. Indeed, using a queue to provide sequencing is a time-tested way of writing concurrent programs that are easily shown to be correct. – Tom Swirly Nov 12 '21 at 12:05
  • As long as you don't need to use SimpleQueue, this is clearly the intended way to use a Queue with an iterator. – 00prometheus May 01 '23 at 12:58
10

For that kind of queue actually I would not typically use this check of queue.empty() because I always use it in a threaded context and thus cannot know whether another thread would put something in there in a few milliseconds (thus that check would be useless anyway). I never check a queue for being empty. I rather use a sentinel value which marks the ending of a producer.

So using the iter(queue.get, Sentinel) is more what I like.

If you know that no other thread will put items in the queue anymore and just want to drain it from all currently contained items, then you can use sth like this:

class Drainer(object):
  def __init__(self, q):
    self.q = q
  def __iter__(self):
    while True:
      try:
        yield self.q.get_nowait()
      except queue.Empty:  # on python 2 use Queue.Empty
        break

for item in Drainer(q):
  print(item)

or

def drain(q):
  while True:
    try:
      yield q.get_nowait()
    except queue.Empty:  # on python 2 use Queue.Empty
      break

for item in drain(q):
  print(item)
Alfe
  • 56,346
  • 20
  • 107
  • 159
  • python 2.7, says `AttributeError: class Queue has no attribute 'Empty'` – majidarif Jan 16 '14 at 09:56
  • 1
    On Python 2.7 after `import Queue` use `Queue.Empty`. Or use `from Queue import Empty` and then only expect `Empty` to be thrown. – Alfe Jan 16 '14 at 10:00
  • The problem on 2.7 is that module and class both have the name `Queue` and depending on your `import` statement you are referring to the one or the other. – Alfe Jan 16 '14 at 10:01
  • Does not work. Does not wait for queue tasks to complete. And specifically what is needed is to check for tasks as they are done. – mathtick Jan 23 '19 at 12:10
  • 1
    @mathtick You are right, there is nothing in there for waiting for a completion of queue tasks. This wasn't asked for. It could be easily added though, so feel free to ask your own question about waiting for completion of queue tasks, there you can give all the details of your specific setup (which seems to be different from the one here). – Alfe Jan 23 '19 at 12:22
  • @alfe I found this post later that was pretty much exactly what I was getting at: https://stackoverflow.com/questions/51911361/analog-as-completed-in-the-multiprocessing-module – mathtick Jan 23 '19 at 21:52
7

My first though was for the iter function, but the built in queue module doesn't return a sentinel, so a good alternative might be to define your own wrapper class:

import Queue

class IterableQueue():
    def __init__(self,source_queue):
            self.source_queue = source_queue
    def __iter__(self):
        while True:
            try:
               yield self.source_queue.get_nowait()
            except Queue.Empty:
               return

This iterator wraps the queue and yields until the queue is empty, then returns, so now you can do:

q = Queue.Queue()
q.put(1)
q.put(2)
q.put(3)

for n in IterableQueue(q):
    print(n)

Output:

1
2
3

This method is a bit verbose it would be interesting if anyone knows anything better using the builtins.

pR0Ps
  • 2,752
  • 2
  • 23
  • 26
James
  • 3,252
  • 1
  • 18
  • 33
  • Best I could come up with is making it a subclass of Queue. Then you can remove the `__init__` method. – mklauber Sep 27 '16 at 18:59
0

I guess this is the easier way

for elem in list(q.queue):
      print(elem)
Ehsan
  • 63
  • 5