2016-07-17 3 views
2

Пинтовый модуль python реализует физические величины. Я хотел бы использовать его вместе с многопроцессорной обработкой. Однако я не знаю, как обращаться с созданием UnitRegistry в новом процессе. Если я интуитивное:Пинтовый модуль Python с многопроцессорной обработкой

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 на дочернем процессе, прежде чем unpickling аргументы. (Инициализация UnitRegistry в функции f не работает, поскольку переменные уже были незакрашены).

Как я могу отправить пинту Количество для дочернего процесса?

Редактировать после ответа Тима Петра:

Проблема заключается в том не привязан к многопроцессорной. Просто засолки количества

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) 

и затем 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') перед рассыпанием. При перекодировании в исходный код для пинты, после создания количества с единицей ms это устройство добавляется во внутренний реестр. Травление использует экземпляр UnitContainer для сохранения единиц. При создании количества через рассыпку устройства в реестр добавляется , а не.

Простым исправлением (в исходном тексте пинты) является изменение функции 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: 

Я открыл проблему на сайте github pint.

+0

Bravo! Все это имеет большой смысл. Я только удивлен, что никто не сталкивался с этим раньше - травление в одном процессе и распитие в другом - это использование соленья _usual_ (даже при отсутствии «многопроцессорности»)! –

ответ

1

Я никогда не использовал pint раньше, но это выглядело интересно ;-) Первое, что я заметил, что у меня нет никаких проблем, если я придерживаюсь единиц явно перечисленных этой линии:

print(dir(ureg.sys.mks)) 

Например, «час "и„второй“оба на выходе, что и ваша программа работает нормально, если Process линия меняется на:

p = Process(target=f, args=(Q(50, 'hour'), Q(50, 'second'))) 

Вы находитесь на Windows, так multiprocessing использует„метод икру“: вся программа импортируется свежей рабочий процесс, в частности:

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

линии были выполнены и в рабочем процессе. Таким образом, реестр единиц - это, инициализированный в рабочем состоянии, но это не тот же (идентичный) реестр, который используется в основной программе - между процессами не распределяется память.

Чтобы получить гораздо глубже, нам действительно нужен эксперт в том, как реализован pint.Моя догадка заключается в том, что для единиц, «составленных» (не на выходе, созданных строкой dir()), путем разбора строк, новый материал добавляется в реестр на каком-то уровне, что необходимо позже для восстановления значений. «ns» и «ms» имеют такой характер: они не в dir() выход.

Ваша программа работает отлично, как есть если добавить следующую строку сразу после Q=ureg.Quantity линии:

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

Это был выстрелом в темноте (на «просвещенной догадке»), который работал: это просто вынудило рабочий процесс разобрать те же «составленные» единицы, которые использовались в основном процессе, попытаться заставить его регистрационный блок в аналогичное состояние.

Надеюсь, есть более чистый способ заставить его работать, но больше не может помочь. Я бы спросил об этом pint авторов.

Смежные вопросы