具有多处理功能的Python pint模块

时间:2016-07-17 03:05:53

标签: python python-multiprocessing pint

python pint模块实现物理量。我想将它与多处理一起使用。但是,我不知道如何处理在新进程中创建UnitRegistry。如果我做直观的话:

from multiprocessing import Process
from pint import UnitRegistry, set_application_registry

ureg = UnitRegistry()
set_application_registry(ureg)
Q = ureg.Quantity


def f(one, two):
    print(one / two)

if __name__ == '__main__':
    p = Process(target=f, args=(Q(50, 'ms'), Q(50, 'ns')))
    p.start()
    p.join()

然后我得到以下异常:

Traceback (most recent call last):
File "C:\WinPython-64bit-3.4.4.2Qt5\python-3.4.4.amd64\lib\multiprocessing\process.py", line 254, in _bootstrap
    self.run()
File "C:\WinPython-64bit-3.4.4.2Qt5\python-3.4.4.amd64\lib\multiprocessing\process.py", line 93, in run
    self._target(*self._args, **self._kwargs)
File "C:\Users\pmaunz\PyCharmProjects\IonControl34\tests\pintmultiprocessing.py", line 12, in f
    print(one / two)
File "C:\WinPython-64bit-3.4.4.2Qt5\python-3.4.4.amd64\lib\site-packages\pint\quantity.py", line 738, in __truediv__
    return self._mul_div(other, operator.truediv)
File "C:\WinPython-64bit-3.4.4.2Qt5\python-3.4.4.amd64\lib\site-packages\pint\quantity.py", line 675, in _mul_div
    offset_units_self = self._get_non_multiplicative_units()
File "C:\WinPython-64bit-3.4.4.2Qt5\python-3.4.4.amd64\lib\site-packages\pint\quantity.py", line 1312, in _get_non_multiplicative_units
    offset_units = [unit for unit in self._units.keys()
File "C:\WinPython-64bit-3.4.4.2Qt5\python-3.4.4.amd64\lib\site-packages\pint\quantity.py", line 1313, in <listcomp>
    if not self._REGISTRY._units[unit].is_multiplicative]
KeyError: 'millisecond'

我认为这是因为在取消参数之前,缺少在子进程上初始化UnitRegistry。 (在函数f中初始化UnitRegistry不起作用,因为变量已经被打开了。)

我如何向儿童流程发送品脱数量?

蒂姆彼得的回答后编辑:

问题是与多处理相关联。简单的酸洗量

from pint import UnitRegistry, set_application_registry
import pickle
ureg = UnitRegistry()
set_application_registry(ureg)
Q = ureg.Quantity
with open("pint.pkl", 'wb') as f:
    pickle.dump(Q(50, 'ms'), f)
    pickle.dump(Q(50, 'ns'), f)

然后在 new 脚本中进行unpickling会导致同样的问题:

from pint import UnitRegistry, set_application_registry 
import pickle
ureg = UnitRegistry()
set_application_registry(ureg)
Q = ureg.Quantity
with open("pint.pkl", 'rb') as f:
    t1 = pickle.load(f)
    t2 = pickle.load(f)

print(t1 / t2)

导致相同的异常。正如蒂姆指出的那样,在展开前添加一行Q(50, 'ns'); Q(50, 'ms')就足够了。在深入了解pint的源代码时,在创建单位为ms的数量时,此单位将添加到内部注册表中。 Pickling使用UnitContainer实例来保存单位。通过unpickling创建数量时, not 已添加到注册表中。

一个简单的修复(在品脱源代码中)是更改函数Quantity.__reduce__以返回一个字符串。

diff --git a/pint/quantity.py b/pint/quantity.py
index 3f30a25..695866a 100644
--- a/pint/quantity.py
+++ b/pint/quantity.py
@@ -57,7 +57,7 @@ class _Quantity(SharedRegistryObject):

     def __reduce__(self):
         from . import _build_quantity
-        return _build_quantity, (self.magnitude, self._units)
+        return _build_quantity, (self.magnitude, str(self._units))

     def __new__(cls, value, units=None):
         if units is None:

我在pint的github网站上打开了一个问题。

1 个答案:

答案 0 :(得分:1)

我之前从未使用pint,但这看起来很有趣;-)我注意到的第一件事是,如果我坚持使用此行明确列出的单位,我没有问题:

print(dir(ureg.sys.mks))

例如,“hour”和“second”都在其输出中,如果Process行更改为:

,则程序运行正常
p = Process(target=f, args=(Q(50, 'hour'), Q(50, 'second')))

你在Windows上,所以multiprocessing正在使用“spawn”方法:整个程序是由工作进程新导入的,所以特别是:

ureg = UnitRegistry()
set_application_registry(ureg)
Q = ureg.Quantity

行也在工作进程中执行。因此单元注册表在工作程序中初始化,但它与主程序中使用的注册表不同(相同) - 进程之间不共享内存。

为了更深入,我们真的需要一位专家来实现pint。我的猜测是,对于单位“组成”(不是在上面的dir()行产生的输出中)通过解析字符串,新的东西会在某个级别添加到注册表中,稍后需要重新构建值。 “ns”和“ms”具有这种性质:它们在dir()输出中

你的程序可以正常运行如果我会在Q=ureg.Quantity行之后立即添加这样的一行:

Q(1, 'ms'); Q(1, 'ns')

这是一个黑暗的镜头(一个“有根据的猜测”),它起作用:它只是强迫工人进程解析主进程中使用的相同“组成”单位,试图强制其< / em>单元注册表进入类似的状态。

我希望有更清洁的方式让它发挥作用,但无法提供更多帮助。我会问pint作者。