3

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)

然后在脚本中解压会导致同样的问题:

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)

导致相同的异常。正如 Tim 所指出的,Q(50, 'ns'); Q(50, 'ms')在 unpickling 之前添加一行就足够了。当挖掘品脱的源代码时,在创建带有单位的数量时,ms该单位被添加到内部注册表中。酸洗使用UnitContainer实例来保存单位。通过 unpickling 创建 Quantity 时,不会将单位添加到注册表中。

一个简单的修复(在 pint 源代码中)是更改函数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 站点上打开了一个问题。

4

1 回答 1

3

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

print(dir(ureg.sys.mks))

例如,“小时”和“秒”都在输出中,如果该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')

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

我希望有一种更清洁的方法可以让它工作,但无能为力。我会问pint作者。

于 2016-07-17T04:57:16.517 回答