在酸洗 numpy 数组的子类时保留自定义属性

2023-12-26

我创建了 numpy ndarray 的子类如下numpy 文档 http://docs.scipy.org/doc/numpy/user/basics.subclassing.html。特别是,我有添加了自定义属性 http://docs.scipy.org/doc/numpy/user/basics.subclassing.html#simple-example-adding-an-extra-attribute-to-ndarray通过修改提供的代码。

我正在使用 Python 在并行循环中操作此类的实例multiprocessing。据我了解,范围本质上“复制”到多个线程的方式是使用pickle.

我现在遇到的问题与 numpy 数组的 pickle 方式有关。我找不到任何关于此的综合文档,但有一些莳萝开发者之间的讨论 https://github.com/uqfoundation/dill/issues/48建议我应该把重点放在__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

Thanks.


np.ndarray uses __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'))

所以,我们得到一个三元组。的文档__reduce__ https://docs.python.org/2/library/pickle.html#object.__reduce__描述每个元素正在做什么:

返回元组时,它必须介于 2 到 5 个元素之间 长的。可选元素可以省略,也可以不提供 作为他们的价值。该元组的内容按正常方式进行腌制,并且 用于在 unpickle 时重建对象。的语义 每个元素是:

  • 一个可调用对象,将被调用以创建初始版本 物体。元组的下一个元素将提供参数 这个可调用的元素和后面的元素提供额外的状态信息 随后将用于完全重建腌制数据。

    在 unpickling 环境中,该对象必须是一个类、一个 可调用注册为“安全构造函数”(见下文),否则它必须 有一个属性__safe_for_unpickling__具有真正的价值。 否则,一个UnpicklingError会在解酸过程中升高 环境。请注意,像往常一样,可调用本身是由 姓名。

  • 可调用对象的参数元组。

  • 可选地,对象的状态将被传递给对象的__setstate__()方法如 Pickling 和 unpickle 普通类实例部分中所述。如果该对象没有__setstate__()方法, 那么,如上所述,该值必须是一个字典,并且它将被添加到 物体的__dict__.

So, _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__, too:

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])

Usage:

>>> 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'
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

在酸洗 numpy 数组的子类时保留自定义属性 的相关文章

随机推荐