multiprocessing.queue 在实例化队列之前缺少模块

multiprocessing.queue module is missing until a Queue is instanced

我在工作 Queues 一切似乎都很顺利。但是,我发现了一种情况,导致出现意外 AttributeError 异常。

让我们创建最简单的代码来创建 Queue。不用担心它的用法,这一切都发生在同一侧,我们不必担心另一侧会与之通信。

import multiprocessing

q = multiprocessing.Queue()
print(type(q))

此代码将产生输出:

<class 'multiprocessing.queues.Queue'>

但是,如果我尝试确定该对象是否是此 class 的实例,会发生什么情况?

isinstance(q, multiprocessing.queues.Queue)

很明显 returns True.

最后,我的问题出现了,如果我没有在检查之前创建一个 Queue 实例,则不会加载定义了 Queue 的模块 multiprocessing.queues ! (警告,由于我们谈论的是模块,所以这里可能加载不合适。我不是这方面的专家)让我们检查一下:

import multiprocessing

a = dict()
isinstance(a, multiprocessing.queues.Queue)

令人惊讶的是我们失败了:

 1 isinstance(a, multiprocessing.queues.Queue)

AttributeError: module 'multiprocessing' has no attribute 'queues'

为什么会这样?这是预期的行为还是这种错误?

对我来说,这代表了一个问题,因为万一我想检查一个对象是否是 Queue 而不是,如果没有其他队列被实例化,我的检查可能会以不希望的 AttributeError.

最后,我想添加一张快速图片,其中包含我复制此内容的一些检查,以及在 multiprocessing.__all__.

中为 queues 模块添加一些检查

这些检查已在 Python 3.9.1 中执行。值得注意的是,当 multiprocessing.queues 已经被识别并且因此检查没有抛出 AttributeError 时,queues 出现在 multiprocessing.__dict__ 中。在其他情况下,它不是。

这对专家来说似乎很自然,因为 __dict__ 是(AFAIK)属性列表,但我想指出这一点,因为正如我所说,我不是专家,我也不知道不知道它的实际含义。

PS。我已将问题标记为 Python 3.8,因为它已经在 3.83.9 中进行了测试,结果相同,如果存在差异,我希望得到 3.8 的答案].由于文档在函数末尾的几个脚注中反映了多处理库的变化,因此可能会出现这些差异。

PS.2。由于 multiprocessing.__dict__ 的输出,我一直将 multiprocessing.queues 称为模块。同样,我不是这方面的专家:

'queues': <module 'multiprocessing.queues' from 'C:\Program Files\Python39\lib\multiprocessing\queues.py'>

这是有意为之的行为。如果你想访问一个子模块,你应该导入它:

import multiprocessing.queues

a = dict()
isinstance(a, multiprocessing.queues.Queue)

导入子模块将它们作为属性添加到它们的父模块中——注意 multiprocessing.queues.Queue 是如何在 queuequeue 之后 queue 上嵌套属性查找 multiprocessing =].显式导入确保子模块在访问之前加载。

The import system: 5.4.2. Submodules

When a submodule is loaded using any mechanism (e.g. importlib APIs, the import or import-from statements, or built-in __import__()) a binding is placed in the parent module’s namespace to the submodule object. For example, if package spam has a submodule foo, after importing spam.foo, spam will have an attribute foo which is bound to the submodule.

这意味着当另一个操作作为副作用导入子模块时,可以跳过显式导出。与所有副作用一样,必须实际执行触发操作才能依赖副作用。

由于导入是幂等的(重复它们没有不良影响),因此应该导入任何期望访问的模块,而不是依赖于其他代码的副作用。