当前位置: 首页 > 面试题库 >

腌制numpy数组的子类时保留自定义属性

东门阳飇
2023-03-14
问题内容

我已经根据numpy文档创建了numpy
ndarray的子​​类。特别是,我通过修改提供的代码添加了自定义属性。

我正在使用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'))

因此,我们得到了一个三元组。用于__reduce__描述每个元素在做什么的文档:

返回一个元组时,它的长度必须在2到5个元素之间。可选元素可以省略,也可以提供None作为其值。将该元组的内容照常进行腌制,并在去腌制时用于重建对象。每个元素的语义是:

* 将被调用以创建对象的初始版本的可调用对象。元组的下一个元素将为此可调用对象提供参数,而后一个元素将提供其他状态信息,这些信息随后将用于完全重建腌制的数据。

在解开环境中,该对象必须是一个类,注册为“安全构造函数”的可调用对象(请参见下文),或者它必须具有一个__safe_for_unpickling__值为true的属性。否则,UnpicklingError将在非酸洗环境中引发。注意,像往常一样,可调用对象本身是按名称腌制的。

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

  • (可选)对象的状态,该状态将传递给对象的
    __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__,以在我们点刺时设置自定义属性。我们只需要确保保留父类需要的所有数据,并调用父类的__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'


 类似资料:
  • 问题内容: 我正在创建一个numpy数组,该数组将填充我制作的特定类的对象。我想初始化数组,使其仅包含该类的对象。例如,这是我想做的事,如果我做这件事会发生什么。 我可以做这个: 然后将的每个元素分配为一个对象(或任何其他类型的对象)。从编程的角度(类型检查)和数学的角度(对函数集进行操作)的角度来看,如果我能够拥有一个s数组,那将是如此的巧妙。 我可以使用任意类指定numpy数组的数据类型吗?

  • 我正在创建一种自定义方言来替换

  • 试图向OpenLDAP添加一个新属性,但总是碰壁。我正在尝试向架构添加ipPhone属性,因为我不能在默认的telephoneNumber属性中包含*数字。 下面是我的LDIF文件,用于创建新属性并将其与objectClass类似。 我已经测试和谷歌了几个小时,但一直无法解决这个问题或找出我错过了什么!

  • 主要内容:ndarray.shape,ndarray.reshape(),ndarray.ndim,ndarray.itemsize,ndarray.flags本节介绍 Numpy 数组的常用属性。 ndarray.shape shape 属性的返回值一个由数组维度构成的元组,比如 2 行 3 列的二维数组可以表示为 ,该属性可以用来调整数组维度的大小。 示例如下,输出了数组的维度: 输出结果: (2,3) 通过 shape 属性修改数组的形状大小:  输出结果: ndarray.reshape

  • 下面链接的文档似乎表明可以对顶级类及其实例进行pickle处理。但根据我对前一个问题的回答来看,这似乎不正确。在我发布的脚本中,pickle接受类对象并写入文件,但这没有用。 这是我的问题:这个文档是错误的,还是有更微妙的东西我不明白?另外,在这种情况下,泡菜是否应该生成某种错误消息? https://docs . python . org/2/library/pickle . html # wh

  • 问题内容: 我正在尝试腌制我定义的(新式)类的对象。但我收到以下错误: 我没有在课堂上明确定义。我做了隐式定义吗?我该如何解决?我需要定义吗? 问题答案: 定义类(而不是)的类可以是您的祖先类,也可以是您的属性或项的类(或祖先类),直接或间接地:本质上,有 向 引用 图 中有任何对象的类具有您的对象将成为根,因为酸洗需要保存整个图形。 解决这个难题的一个简单方法是使用protocol ,这意味着“