我创建了以下numpy的ndarray的子类的numpy的文档 。 具体地讲,我已添加了一个自定义属性通过修改所提供的代码。
我操纵这个类的实例并行循环中,使用Python multiprocessing
。 据我了解,该范围基本上“复制”到多个线程的方法是用pickle
。
我现在来面对这个问题涉及numpy的阵列腌渍的方式。 我找不到这方面有任何全面的文档,但有些萝开发者之间的讨论,建议我应该着重于__reduce__
方法,它被酸洗时调用。
任何人都可以摆脱任何这更多的光线? 最低工作例子其实只是numpy的示例代码我挂上面,这里复制的完整性:
import numpy as np
class RealisticInfoArray(np.ndarray):
def __new__(cls, input_array, info=None):
# Input array is an already formed ndarray instance
# We first cast to be our class type
obj = np.asarray(input_array).view(cls)
# add the new attribute to the created instance
obj.info = info
# Finally, we must return the newly created object:
return obj
def __array_finalize__(self, obj):
# see InfoArray.__array_finalize__ for comments
if obj is None: return
self.info = getattr(obj, 'info', None)
现在,这里的问题是:
import pickle
obj = RealisticInfoArray([1, 2, 3], info='foo')
print obj.info # 'foo'
pickle_str = pickle.dumps(obj)
new_obj = pickle.loads(pickle_str)
print new_obj.info # raises AttributeError
谢谢。
np.ndarray
使用__reduce__
腌本身。 我们可以看看,当你调用函数来获取这是怎么回事的一个想法是什么,它实际上返回:
>>> obj = RealisticInfoArray([1, 2, 3], info='foo')
>>> obj.__reduce__()
(<built-in function _reconstruct>, (<class 'pick.RealisticInfoArray'>, (0,), 'b'), (1, (3,), dtype('int64'), False, '\x01\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00'))
所以,我们得到了一个3元组回。 对于文档__reduce__
描述一下每个元素做:
当返回一个元组,它必须是长二五行之间。 可选的元素可以被省略,或者可以无作为其值来提供。 该元组的内容被酸洗正常和用于重构在在unpickle时间的对象。 每个元素的语义如下:
将被调用来创建对象的初始版本可调用对象。 该元组的下一个元素将为此可调用提供的参数,和后面的元素提供随后将被用于完全重构腌渍数据的附加状态信息。
在在unpickle环境中,该对象必须是一个类,可调用注册为“安全构造”(见下文),或者必须有一个属性__safe_for_unpickling__
具有真正的价值。 否则, UnpicklingError
将在在unpickle环境得到提升。 需要注意的是像往常一样,可调用本身是由名腌制。
的论据,可调用对象的元组。
可选地,对象的状态,这将被传递给对象的__setstate__()
如第酸洗描述并取储存正常类实例方法。 如果对象没有__setstate__()
方法,然后,如上所述,该值必须是一个字典,它将被添加到该对象的__dict__
。
所以, _reconstruct
是称为重建对象的函数(<class 'pick.RealisticInfoArray'>, (0,), 'b')
是传递到该函数的参数,以及(1, (3,), dtype('int64'), False, '\x01\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00'))
被传递到类' __setstate__
。 这给了我们一个机会; 我们可以覆盖__reduce__
并提供我们自己的元组__setstate__
,然后另外重写__setstate__
,来设置我们的自定义属性,当我们unpickle。 我们只需要确保我们保留了所有的父类所需要的数据,并调用父的__setstate__
,太:
class RealisticInfoArray(np.ndarray):
def __new__(cls, input_array, info=None):
obj = np.asarray(input_array).view(cls)
obj.info = info
return obj
def __array_finalize__(self, obj):
if obj is None: return
self.info = getattr(obj, 'info', None)
def __reduce__(self):
# Get the parent's __reduce__ tuple
pickled_state = super(RealisticInfoArray, self).__reduce__()
# Create our own tuple to pass to __setstate__
new_state = pickled_state[2] + (self.info,)
# Return a tuple that replaces the parent's __setstate__ tuple with our own
return (pickled_state[0], pickled_state[1], new_state)
def __setstate__(self, state):
self.info = state[-1] # Set the info attribute
# Call the parent's __setstate__ with the other tuple elements.
super(RealisticInfoArray, self).__setstate__(state[0:-1])
用法:
>>> obj = pick.RealisticInfoArray([1, 2, 3], info='foo')
>>> pickle_str = pickle.dumps(obj)
>>> pickle_str
"cnumpy.core.multiarray\n_reconstruct\np0\n(cpick\nRealisticInfoArray\np1\n(I0\ntp2\nS'b'\np3\ntp4\nRp5\n(I1\n(I3\ntp6\ncnumpy\ndtype\np7\n(S'i8'\np8\nI0\nI1\ntp9\nRp10\n(I3\nS'<'\np11\nNNNI-1\nI-1\nI0\ntp12\nbI00\nS'\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x00\\x00\\x00\\x00'\np13\nS'foo'\np14\ntp15\nb."
>>> new_obj = pickle.loads(pickle_str)
>>> new_obj.info
'foo'
我的dill
(和pathos
)的作者。 dill
是酸洗numpy.array
前numpy
能做到这一点本身。 @端午的解释是相当准确的。 我个人而言,我只是用dill
,让它为你做的工作。 随着dill
,你不需要__reduce__
,如dill
有它抓住子类的属性......其中一个是存储几种方式__dict__
任何类对象。 pickle
不这样做,B / C,通常按名称引用带班工作,而不是存储类对象本身......所以你必须一起工作__reduce__
使pickle
为你工作。 没有必要,在大多数情况下,用dill
。
>>> import numpy as np
>>>
>>> class RealisticInfoArray(np.ndarray):
... def __new__(cls, input_array, info=None):
... # Input array is an already formed ndarray instance
... # We first cast to be our class type
... obj = np.asarray(input_array).view(cls)
... # add the new attribute to the created instance
... obj.info = info
... # Finally, we must return the newly created object:
... return obj
... def __array_finalize__(self, obj):
... # see InfoArray.__array_finalize__ for comments
... if obj is None: return
... self.info = getattr(obj, 'info', None)
...
>>> import dill as pickle
>>> obj = RealisticInfoArray([1, 2, 3], info='foo')
>>> print obj.info # 'foo'
foo
>>>
>>> pickle_str = pickle.dumps(obj)
>>> new_obj = pickle.loads(pickle_str)
>>> print new_obj.info
foo
dill
本身可以延伸到pickle
(主要由copy_reg
它什么都知道),这样的话,你可以使用所有dill
类型在使用任何pickle
。 现在,如果你要使用multiprocessing
,你有点拧,因为它使用cPickle
。 然而,有,在pathos
的叉multiprocessing
(称为pathos.multiprocessing
),基本上唯一的变化是它采用dill
代替cPickle
......,因此可以在一个序列化有很多的赫克更Pool.map
。 我认为,(目前),如果你想与你的一个子类的工作numpy.array
在multiprocessing
(或pathos.multiprocessing
),你可能需要做一些像@dano建议-但不知道,因为我没想到的良好的情况下把我的头顶部,以测试你的子类。
如果你有兴趣,得到pathos
这里: https://github.com/uqfoundation