【问题标题】:multiprocessing.queue module is missing until a Queue is instancedmultiprocessing.queue 模块丢失,直到 Queue 被实例化
【发布时间】:2021-02-10 18:14:31
【问题描述】:

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

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

import multiprocessing

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

此代码将作为输出产生:

<class 'multiprocessing.queues.Queue'>

但是如果我尝试识别对象是否是此类的实例会发生什么?

isinstance(q, multiprocessing.queues.Queue)

显然它返回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'>

【问题讨论】:

  • 如果你想使用multiprocessing.queues,你必须import multiprocessing.queues。依赖其他东西为你做一个子模块导入总是一个坏主意,不仅仅是这个特定的子模块。
  • 您能详细说明一下吗?澄清常规模块导入与其子模块的行为方式之间的差异似乎是理解正在发生的事情的关键——而不是导入子模块。将是一个完美的答案。无论如何,感谢您指出这一点,因为更改导入解决了问题中暴露的行为!

标签: python-3.x python-multiprocessing python-3.8


【解决方案1】:

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

import multiprocessing.queues

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

导入子模块会将它们作为属性添加到其父模块 - 请注意 multiprocessing.queues.Queue 如何是在 multiprocessing 上的 queuequeue 上的 Queue 的嵌套属性查找。显式导入确保子模块在访问之前被加载。

导入系统:5.4.2. Submodules

当使用任何机制(例如importlib API、importimport-from 语句或内置__import__())加载子模块时,将在父模块的命名空间中放置与子模块对象的绑定。例如,如果包spam有一个子模块foo,在导入spam.foo后,spam会有一个属性foo绑定到子模块。

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

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

【讨论】:

    猜你喜欢
    • 2017-09-12
    • 2016-11-07
    • 1970-01-01
    • 1970-01-01
    • 2015-05-05
    • 1970-01-01
    • 2020-02-05
    • 2011-04-23
    • 2012-06-01
    相关资源
    最近更新 更多