在原地更改子类的`ndarray`视图

时间:2016-01-20 09:53:23

标签: python numpy

假设我有一个简单的Test类,它继承自numpy.ndarraydocumentation on ndarray subclassing)。此类创建一个对象,该对象是另一个ndarray的局部视图。现在我想创建一个可以就地修改此视图的方法。类似的东西:

import numpy


class Test(numpy.ndarray):

    def __new__(cls, info='Test class'):
        base = numpy.ndarray.__new__(cls, (6, 2), dtype=float)
        view = base[0:2, :]
        view.info = info
        return view

    def __array_finalize__(self, obj):
        if obj is None:
            return
        self.info = getattr(obj, 'info', None)

    def change_view(self, start, stop):
        """This is the method I'd like to implement"""
        # Incorrect, but represents what I am looking for
        self = self.base[start:stop]


if __name__ == '__main__':
    t = Test()
    print(t)
    print(t.info)
    t.change_view(1, 5)
    print(t)
    print(t.info)

我希望方法.change_view(x)修改.base属性的视图以显示行[1:5]而不是[0:2],这是__new__()中的默认值}。

这可能吗?如果是这样,怎么样?否则,为什么不呢?请注意,.base永远不会更改(它总是占用相同的内存空间),我只想更改它的视图。

备注

  • 请参阅change_view()中的评论。我知道self = self.base[start:stop]不正确,我知道并理解为什么,但我认为这是一种表达我所寻找内容的简短方法。
  • 如下面的答案中所述,人们可以想到self[:] = ...self.data = ...。但是,在更改视图大小时这将不起作用(情况确实如此)。

参考

这个问题was already asked here(或者至少是类似的情况),但答案提到了返回修改后的数组的解决方案(即没有就地修改)或者使用了包装器对象(即具有属性)使用要修改的数组。)

我知道这些可能性,我知道它们可以更简单甚至更方便。但是,我想了解为什么(如果是这样的话)不可能做我正在寻找的事情。

2 个答案:

答案 0 :(得分:2)

1)您无法更改ndarray实例或子类实例指向的就地数据。抱歉!事实证明,有可能更改ndarray实例指向的数据,但这是一个可怕的想法,希望很快就会删除:-)。见https://github.com/numpy/numpy/issues/7093

2)您尝试分配给self这样只会改变方法本地绑定中self变量的绑定,我担心这表明对Python的方式有一个相当基本的误解执行和对象模型工作。我只是提出这个问题,因为成功继承ndarray hard 真的很难。探究不可能。 (例如,受欢迎的pandas库的开发人员放弃了。)尽管该文档给出了误导性的印象,但numpy并不是真正设计用于支持子类化,并且它不能很好地工作。我通常建议没有人试图继承ndarray;如果你还不是一个专业的Python程序员,那么这个问题要好十倍。可能我们会删除那个愚蠢的页面并完全禁用子类,除了它会破坏向后兼容性:-(。我建议找一个不同的策略来解决你的问题。

答案 1 :(得分:1)

编写

之类的代码时
var = value

它为var分配新值并删除旧值,但这仅在当前范围内为真,并且前一个值var的所有其他别名保持不变。 Python的self并不特别,它只是一个变量,所以当你写的时候

self = self.base[start:stop]

您只需将self.base[start:stop]的值分配给局部变量self,但self中预先包含的值不会更改以及所有其他值的引用(这是一个实例你要改变的Test类)。您可以使用change_view的以下实现来测试它:

def change_view(self, start, stop):
    """This is the method I'd like to implement"""
    # Incorrect, but represents what I am looking for
    print('before assignment')
    print(self)
    self = self.base[start:stop]
    print('after assignment')
    print(self)
    print('end change_view')

我认为使用此实现运行代码应该清除一些东西。 可能的实现可能与此类似:

def change_view(self, start, stop):
    """This is the method I'd like to implement"""
    # Incorrect, but represents what I am looking for
    self[:] = self.base[start:stop]

重要的区别在于它改变了变量self中包含的值,而不是为变量赋值。我认为这样的事情应该适用于python列表,但numpy.ndarray更复杂。此外,由于它在C中实施,它可能根本不可能。例如,您需要以某种方式调整视图大小,但是您无法使用numpy.ndarray.resize,因为视图不拥有它的数据;简单地指定sizeshape也不会起作用。