在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)
在某个地方有文档可能指向为什么做出这种设计选择吗?这似乎很奇怪,尤其是当the source code显示它是建立在collections.deque
之上时(请注意,Queue不会从deque
继承)
答案 0 :(得分:3)
根据truth value testing过程的定义,此行为是预期的:
可以测试任何对象的真值,以便在if或while中使用 条件或以下布尔运算的操作数。
默认情况下,除非对象的类定义 返回False的
__bool__()
方法或__len__()
方法 与对象一起调用时返回零。
由于Queue既未实现__bool__()
也未实现__len__()
,因此其真值是True
。关于为什么Queue不实现__len__()
的原因,可以在qsize函数的注释中找到线索:
'''返回队列的大概大小(不可靠!)。'
__bool__()
函数也是如此。
答案 1 :(得分:0)
我将按原样保留接受的答案,但据我所知,原因是if _queue: # do something
会成为竞赛条件,因为{{1} }旨在在线程之间传递,因此就任务执行而言,它具有可疑状态。
来源:
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
布尔值一旦被评估,就不会与队列的状态联系在一起。因此,用户正在基于已经过时的状态针对队列进行处理。
就像检查文件的存在一样,处理异常更像pythonic:
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
我认为作者故意放弃try:
with open(file) as fh:
# do processing
except FileNotFoundError as e:
# do something else
方法的目的是避免开发人员 反对这种范例,并像对待其他队列一样对待队列。状态可能有问题的物体。