25

在与某人就 Python 中的异常处理进行了简短的辩论之后——由队列对象的处理引发——我想我会把它扔在那里......

方法一:

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()

方法二:

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:我们俩都不知道队列对象是线程安全的。看起来尝试/除了是要走的路!

4

3 回答 3

51

方法 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”子句来缩小例外子句的范围。

于 2012-06-28T15:17:50.360 回答
6

如果这是多线程/多处理代码(这是使用队列的一个很好的理由),那么绝对是方法 1。在q.empty()通话和q.get()通话之间,红桃杰克可能会偷走你的馅饼!

于 2012-06-28T15:04:35.867 回答
5

一种说法是方法 1 是错误的,因为队列为空不是错误,因此不应使用 Queue.Empty 异常处理

异常不一定是“错误”,它是一种通用的流控制机制,并且在少数情况下确实以这种方式使用(SysExit、StopIteration 等)。

这里的好问题是:最常见的情况是什么 - 空队列或非空队列。除非您确定,否则您想要 AskBeforeYouLeap,因为它很可能更便宜。

于 2012-06-28T15:11:15.603 回答