【问题标题】:When subclassing ndarray why does a transpose happen after __array_finalize__ and not before?当子类化ndarray时,为什么在__array_finalize__之后而不是之前发生转置?
【发布时间】:2020-03-16 23:29:25
【问题描述】:

为简单起见,我们只需从 numpy 文档中复制诊断 ndarray 子类:

import numpy as np

class MySubClass(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):
        print('In __array_finalize__:')
        print('   self is %s' % repr(self))
        print('   obj is %s' % repr(obj))
        if obj is None: return
        self.info = getattr(obj, 'info', None)

现在我们做一个简单的例子:

>>> x = MySubClass(np.ones((1,5)))
In __array_finalize__:
   self is MySubClass([[1., 1., 1., 1., 1.]])
   obj is array([[1., 1., 1., 1., 1.]])
>>> y = x.T
In __array_finalize__:
   self is MySubClass([[1., 1., 1., 1., 1.]])
   obj is MySubClass([[1., 1., 1., 1., 1.]])

我们可以看到,显然不是转置的东西被传递给__array_finalize__。除了将“finalize”这个词的含义扩展到全新的领域之外,这种行为的目的是什么?

发送实际输出不是更有意义吗,即通过这个钩子进行转置以使其最终确定?

用我的子类可能需要的任何后处理来修饰基本转置的推荐方法是什么?

【问题讨论】:

  • 要明确一点,y 仍然是 x 的转置,info 按预期设置。在我自己的测试中,某些方法在进行更改之前显然调用了finalize(例如这个转置),但对于其他方法,self 已经更改(例如索引)
  • 显然finalize 在创建实例时被调用(通过new/init 或某种view),但之后应用了一些处理,如转置。 ufunc 似乎创建了正确子类、形状和 dtype 的 empty,并将其用作 out,在 finalize 之后分配值。

标签: python numpy subclassing


【解决方案1】:

这是因为在创建新对象时,他们依赖已经可用的(通用)函数PyArray_NewFromDescrAndBase 来处理内存分配。 PyArray_Transpose 的源代码显示,首先新对象是从现有数组创建的,具有相似的形状和步幅,然后通过访问先前分配的内存进行纠正:

/*
 * this allocates memory for dimensions and strides (but fills them
 * incorrectly), sets up descr, and points data at PyArray_DATA(ap).
 */
Py_INCREF(PyArray_DESCR(ap));
ret = (PyArrayObject *) PyArray_NewFromDescrAndBase(
        Py_TYPE(ap), PyArray_DESCR(ap),
        n, PyArray_DIMS(ap), NULL, PyArray_DATA(ap),
        flags, (PyObject *)ap, (PyObject *)ap);
if (ret == NULL) {
    return NULL;
}

/* fix the dimensions and strides of the return-array */
for (i = 0; i < n; i++) {
    PyArray_DIMS(ret)[i] = PyArray_DIMS(ap)[permutation[i]];
    PyArray_STRIDES(ret)[i] = PyArray_STRIDES(ap)[permutation[i]];
}

这里PyArray_NewFromDescrAndBase 负责invoking __array_finalize__,因此该方法接收的版本具有不正确的形状和步幅(即非转置)。可以做不同的事情,但需要为PyArray_NewFromDescrAndBase 提供一个额外的参数来推迟对__array_finalize__ 的调用,然后可以在调整形状和步幅后手动完成。

【讨论】:

  • 谢谢。 PyArray_NewFromDescrAndBase 似乎带有 dimsstrides 参数,所以在调用它之前很容易转置,对吧?
  • @PaulPanzer 确实,这似乎是一种可能性。但这需要预先分配内存,而当前的解决方案可以避免这种情况。也许没有人考虑过与__array_finalize__ 的交互(这可能是稍后介绍的,虽然我无法使用git blame 找到确定的日期),而当前版本似乎是一个简单的解决方案。
猜你喜欢
  • 1970-01-01
  • 2013-02-04
  • 2020-02-07
  • 2011-04-10
  • 2023-03-03
  • 2021-09-24
  • 2020-07-20
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多