当子类化 ndarray 时,为什么转置发生在 __array_finalize__ 之后而不是之前?

Pau*_*zer 5 python numpy subclassing

为简单起见,让我们从 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)
Run Code Online (Sandbox Code Playgroud)

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

>>> 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.]])
Run Code Online (Sandbox Code Playgroud)

正如我们所看到的,显然不是转置的东西被传递给__array_finalize__. 除了将“最终确定”一词的含义扩展到全新领域之外,这种行为的目的是什么?

发送实际输出是否更有意义,即通过此挂钩进行转置以使其最终确定?

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

a_g*_*est 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]];
}
Run Code Online (Sandbox Code Playgroud)

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